%{
// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Initial yacc source generated by ebnf2y[1]
// at 2013-10-04 23:10:47.861401015 +0200 CEST
//
//  $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _
//
//   [1]: http://github.com/cznic/ebnf2y

package parser

import (
	"strings"

	"github.com/pingcap/tidb/parser/mysql"
	"github.com/pingcap/tidb/parser/ast"
	"github.com/pingcap/tidb/parser/model"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/parser/charset"
	"github.com/pingcap/tidb/parser/types"
)

%}

%union {
	offset int // offset
	item interface{}
	ident string
	expr ast.ExprNode
	statement ast.StmtNode
}

%token	<ident>
	/*yy:token "%c"     */	identifier      "identifier"
	/*yy:token "_%c"    */  underscoreCS	"UNDERSCORE_CHARSET"
	/*yy:token "\"%c\"" */	stringLit       "string literal"
	singleAtIdentifier			"identifier with single leading at"
	doubleAtIdentifier			"identifier with double leading at"
	invalid					"a special token never used by parser, used by lexer to indicate error"
	hintBegin				"hintBegin is a virtual token for optimizer hint grammar"
	hintEnd					"hintEnd is a virtual token for optimizer hint grammar"
	andand					"&&"
	pipes					"||"

	/* The following tokens belong to ODBCDateTimeType. */
	odbcDateType			"d"
	odbcTimeType			"t"
	odbcTimestampType		"ts"

	/* The following tokens belong to ReservedKeyword. Notice: make sure these tokens are contained in ReservedKeyword. */
	add			"ADD"
	all 			"ALL"
	alter			"ALTER"
	analyze			"ANALYZE"
	and			"AND"
	as			"AS"
	asc			"ASC"
	between			"BETWEEN"
	bigIntType		"BIGINT"
	binaryType		"BINARY"
	blobType		"BLOB"
	both			"BOTH"
	by			"BY"
	cascade			"CASCADE"
	caseKwd			"CASE"
	change        		"CHANGE"
	character		"CHARACTER"
	charType		"CHAR"
	check 			"CHECK"
	collate 		"COLLATE"
	column			"COLUMN"
	constraint		"CONSTRAINT"
	convert			"CONVERT"
	create			"CREATE"
	cross 			"CROSS"
	currentDate 		"CURRENT_DATE"
	currentTime 		"CURRENT_TIME"
	currentTs		"CURRENT_TIMESTAMP"
	currentUser		"CURRENT_USER"
	currentRole		"CURRENT_ROLE"
	database		"DATABASE"
	databases		"DATABASES"
	dayHour			"DAY_HOUR"
	dayMicrosecond		"DAY_MICROSECOND"
	dayMinute		"DAY_MINUTE"
	daySecond 		"DAY_SECOND"
	decimalType		"DECIMAL"
	defaultKwd		"DEFAULT"
	delayed			"DELAYED"
	deleteKwd		"DELETE"
	desc			"DESC"
	describe		"DESCRIBE"
	distinct		"DISTINCT"
	distinctRow		"DISTINCTROW"
	div 			"DIV"
	doubleType		"DOUBLE"
	drop			"DROP"
	dual 			"DUAL"
	elseKwd			"ELSE"
	enclosed		"ENCLOSED"
	escaped 		"ESCAPED"
	exists			"EXISTS"
	explain			"EXPLAIN"
	except			"EXCEPT"
	falseKwd		"FALSE"
	floatType		"FLOAT"
	forKwd			"FOR"
	force			"FORCE"
	foreign			"FOREIGN"
	from			"FROM"
	fulltext		"FULLTEXT"
	generated		"GENERATED"
	grant			"GRANT"
	group			"GROUP"
	having			"HAVING"
	highPriority		"HIGH_PRIORITY"
	hourMicrosecond		"HOUR_MICROSECOND"
	hourMinute		"HOUR_MINUTE"
	hourSecond		"HOUR_SECOND"
	ifKwd			"IF"
	ignore			"IGNORE"
	in			"IN"
	index			"INDEX"
	infile			"INFILE"
	inner 			"INNER"
	integerType		"INTEGER"
	interval		"INTERVAL"
	into			"INTO"
	is			"IS"
	insert			"INSERT"
	intType			"INT"
	int1Type		"INT1"
	int2Type		"INT2"
	int3Type		"INT3"
	int4Type		"INT4"
	int8Type		"INT8"
	join			"JOIN"
	key			"KEY"
	keys			"KEYS"
	kill			"KILL"
	language		"LANGUAGE"
	leading			"LEADING"
	left			"LEFT"
	like			"LIKE"
	limit			"LIMIT"
	lines 			"LINES"
	linear			"LINEAR"
	load			"LOAD"
	localTime		"LOCALTIME"
	localTs			"LOCALTIMESTAMP"
	lock			"LOCK"
	longblobType		"LONGBLOB"
	longtextType		"LONGTEXT"
	lowPriority		"LOW_PRIORITY"
	match			"MATCH"
	maxValue		"MAXVALUE"
	mediumblobType		"MEDIUMBLOB"
	mediumIntType		"MEDIUMINT"
	mediumtextType		"MEDIUMTEXT"
	minuteMicrosecond	"MINUTE_MICROSECOND"
	minuteSecond 		"MINUTE_SECOND"
	mod 			"MOD"
	not			"NOT"
	noWriteToBinLog 	"NO_WRITE_TO_BINLOG"
	null			"NULL"
	numericType		"NUMERIC"
	nvarcharType		"NVARCHAR"
	on			"ON"
	optimize		"OPTIMIZE"
	option			"OPTION"
	optionally		"OPTIONALLY"
	or			"OR"
	order			"ORDER"
	outer			"OUTER"
	packKeys		"PACK_KEYS"
	partition		"PARTITION"
	parser			"PARSER"
	precisionType		"PRECISION"
	primary			"PRIMARY"
	procedure		"PROCEDURE"
	shardRowIDBits		"SHARD_ROW_ID_BITS"
	preSplitRegions		"PRE_SPLIT_REGIONS"
	rangeKwd		"RANGE"
	read			"READ"
	realType		"REAL"
	references		"REFERENCES"
	regexpKwd		"REGEXP"
	rename         		"RENAME"
	repeat			"REPEAT"
	replace			"REPLACE"
	require			"REQUIRE"
	restrict		"RESTRICT"
	revoke			"REVOKE"
	right			"RIGHT"
	rlike			"RLIKE"
	row			"ROW"
	secondMicrosecond	"SECOND_MICROSECOND"
	selectKwd		"SELECT"
	set			"SET"
	show			"SHOW"
	smallIntType		"SMALLINT"
	spatial			"SPATIAL"
	sql			"SQL"
	sqlBigResult		"SQL_BIG_RESULT"
	sqlCalcFoundRows	"SQL_CALC_FOUND_ROWS"
	sqlSmallResult		"SQL_SMALL_RESULT"
	ssl			"SSL"
	starting		"STARTING"
	straightJoin		"STRAIGHT_JOIN"
	tableKwd		"TABLE"
	stored			"STORED"
	terminated		"TERMINATED"
	then			"THEN"
	tinyblobType		"TINYBLOB"
	tinyIntType		"TINYINT"
	tinytextType		"TINYTEXT"
	to			"TO"
	trailing		"TRAILING"
	trigger			"TRIGGER"
	trueKwd			"TRUE"
	unique			"UNIQUE"
	union			"UNION"
	unlock			"UNLOCK"
	unsigned		"UNSIGNED"
	until			"UNTIL"
	update			"UPDATE"
	usage			"USAGE"
	use			"USE"
	using			"USING"
	utcDate 		"UTC_DATE"
	utcTimestamp		"UTC_TIMESTAMP"
	utcTime			"UTC_TIME"
	values			"VALUES"
	long			"LONG"
	varcharType		"VARCHAR"
	varcharacter		"VARCHARACTER"
	varbinaryType		"VARBINARY"
	varying			"VARYING"
	virtual			"VIRTUAL"
	when			"WHEN"
	where			"WHERE"
	write			"WRITE"
	with			"WITH"
	xor 			"XOR"
	yearMonth		"YEAR_MONTH"
	zerofill		"ZEROFILL"
	natural			"NATURAL"

	/* The following tokens belong to UnReservedKeyword. Notice: make sure these tokens are contained in UnReservedKeyword. */
	account		"ACCOUNT"
	action		"ACTION"
	advise		"ADVISE"
	after		"AFTER"
	against		"AGAINST"
	always		"ALWAYS"
	algorithm	"ALGORITHM"
	any 		"ANY"
	ascii		"ASCII"
	autoIncrement	"AUTO_INCREMENT"
	autoRandom	"AUTO_RANDOM"
	avgRowLength	"AVG_ROW_LENGTH"
	avg		"AVG"
	begin		"BEGIN"
	binlog		"BINLOG"
	bitType		"BIT"
	block		"BLOCK"
	booleanType	"BOOLEAN"
	boolType	"BOOL"
	btree		"BTREE"
	byteType	"BYTE"
	cache           "CACHE"
	cascaded	"CASCADED"
	capture		"CAPTURE"
	charsetKwd	"CHARSET"
	checksum	"CHECKSUM"
	cipher		"CIPHER"
	cleanup		"CLEANUP"
	client		"CLIENT"
	coalesce	"COALESCE"
	collation	"COLLATION"
	columnFormat	"COLUMN_FORMAT"
	columns		"COLUMNS"
	comment 	"COMMENT"
	commit		"COMMIT"
	committed	"COMMITTED"
	compact		"COMPACT"
	compressed	"COMPRESSED"
	compression	"COMPRESSION"
	connection 	"CONNECTION"
	consistent	"CONSISTENT"
	context		"CONTEXT"
	cpu		"CPU"
	current		"CURRENT"
	cycle           "CYCLE"
	day		"DAY"
	data 		"DATA"
	dateType	"DATE"
	datetimeType	"DATETIME"
	deallocate	"DEALLOCATE"
	definer		"DEFINER"
	delayKeyWrite	"DELAY_KEY_WRITE"
	directory	"DIRECTORY"
	disable		"DISABLE"
	discard		"DISCARD"
	disk		"DISK"
	do		"DO"
	duplicate	"DUPLICATE"
	dynamic		"DYNAMIC"
	enable		"ENABLE"
	encryption	"ENCRYPTION"
	end		"END"
	engine		"ENGINE"
	engines		"ENGINES"
	enum 		"ENUM"
	event		"EVENT"
	events		"EVENTS"
	evolve		"EVOLVE"
	escape 		"ESCAPE"
	exchange	"EXCHANGE"
	exclusive       "EXCLUSIVE"
	execute		"EXECUTE"
	expansion	"EXPANSION"
	expire		"EXPIRE"
	extended	"EXTENDED"
	faultsSym	"FAULTS"
	fields		"FIELDS"
	first		"FIRST"
	fixed		"FIXED"
	flush		"FLUSH"
	following	"FOLLOWING"
	format		"FORMAT"
	full		"FULL"
	function	"FUNCTION"
	grants		"GRANTS"
	hash		"HASH"
	history		"HISTORY"
	hosts		"HOSTS"
	hour		"HOUR"
	identified	"IDENTIFIED"
	importKwd	"IMPORT"
	insertMethod 	"INSERT_METHOD"
	isolation	"ISOLATION"
	issuer		"ISSUER"
	increment       "INCREMENT"
	incremental	"INCREMENTAL"
	indexes		"INDEXES"
	invisible	"INVISIBLE"
	invoker		"INVOKER"
	io		"IO"
	ipc		"IPC"
	jsonType	"JSON"
	keyBlockSize	"KEY_BLOCK_SIZE"
	labels		"LABELS"
	last		"LAST"
	less		"LESS"
	level		"LEVEL"
	list		"LIST"
	local		"LOCAL"
	location	"LOCATION"
	logs		"LOGS"
	master		"MASTER"
	microsecond	"MICROSECOND"
	minute		"MINUTE"
	mode		"MODE"
	modify		"MODIFY"
	month		"MONTH"
	maxRows		"MAX_ROWS"
	maxConnectionsPerHour	"MAX_CONNECTIONS_PER_HOUR"
	maxQueriesPerHour	"MAX_QUERIES_PER_HOUR"
	maxUpdatesPerHour	"MAX_UPDATES_PER_HOUR"
	maxUserConnections	"MAX_USER_CONNECTIONS"
	memory		"MEMORY"
	merge		"MERGE"
	minRows		"MIN_ROWS"
	minValue        "MINVALUE"
	max_minutes	"MAX_MINUTES"
	max_idxnum	"MAX_IDXNUM"
	names		"NAMES"
	national	"NATIONAL"
	ncharType	"NCHAR"
	never		"NEVER"
	no		"NO"
	nocache         "NOCACHE"
	nocycle         "NOCYCLE"
	nodegroup	"NODEGROUP"
	nomaxvalue      "NOMAXVALUE"
	nominvalue      "NOMINVALUE"
	none		"NONE"
	noorder         "NOORDER"
	nulls		"NULLS"
	offset		"OFFSET"
	only		"ONLY"
	pageSym		"PAGE"
	password	"PASSWORD"
	partial		"PARTIAL"
	partitioning	"PARTITIONING"
	partitions	"PARTITIONS"
	pipesAsOr
	plugins		"PLUGINS"
	preceding	"PRECEDING"
	prepare		"PREPARE"
	privileges	"PRIVILEGES"
	process		"PROCESS"
	processlist	"PROCESSLIST"
	profile		"PROFILE"
	profiles	"PROFILES"
	per_table	"PER_TABLE"
	per_db		"PER_DB"
	quarter		"QUARTER"
	query		"QUERY"
	queries		"QUERIES"
	quick		"QUICK"
	rebuild 	"REBUILD"
	recover 	"RECOVER"
	redundant	"REDUNDANT"
	reload		"RELOAD"
	remove 		"REMOVE"
	reorganize	"REORGANIZE"
	repair		"REPAIR"
	repeatable	"REPEATABLE"
	respect		"RESPECT"
	replica		"REPLICA"
	replication	"REPLICATION"
	reverse		"REVERSE"
	role		"ROLE"
	rollback	"ROLLBACK"
	routine		"ROUTINE"
	rowCount	"ROW_COUNT"
	rowFormat	"ROW_FORMAT"
	rtree		"RTREE"
	second		"SECOND"
	secondaryEngine	"SECONDARY_ENGINE"
	secondaryLoad	"SECONDARY_LOAD"
	secondaryUnload	"SECONDARY_UNLOAD"
	security	"SECURITY"
	separator 	"SEPARATOR"
	sequence        "SEQUENCE"
	serial		"SERIAL"
	serializable	"SERIALIZABLE"
	session		"SESSION"
	share		"SHARE"
	shared		"SHARED"
	shutdown	"SHUTDOWN"
	signed		"SIGNED"
	simple		"SIMPLE"
	slave		"SLAVE"
	slow		"SLOW"
	snapshot	"SNAPSHOT"
	sqlBufferResult	"SQL_BUFFER_RESULT"
	sqlCache	"SQL_CACHE"
	sqlNoCache	"SQL_NO_CACHE"
	sqlTsiDay	"SQL_TSI_DAY"
	sqlTsiHour	"SQL_TSI_HOUR"
	sqlTsiMinute	"SQL_TSI_MINUTE"
	sqlTsiMonth	"SQL_TSI_MONTH"
	sqlTsiQuarter	"SQL_TSI_QUARTER"
	sqlTsiSecond	"SQL_TSI_SECOND"
	sqlTsiWeek	"SQL_TSI_WEEK"
	sqlTsiYear	"SQL_TSI_YEAR"
	start		"START"
	statsAutoRecalc	"STATS_AUTO_RECALC"
	statsPersistent	"STATS_PERSISTENT"
	statsSamplePages	"STATS_SAMPLE_PAGES"
	status		"STATUS"
	storage		"STORAGE"
	swaps		"SWAPS"
	switchesSym	"SWITCHES"
	systemTime	"SYSTEM_TIME"
	open		"OPEN"
	source	   	"SOURCE"
	subject		"SUBJECT"
	subpartition	"SUBPARTITION"
	subpartitions	"SUBPARTITIONS"
	super		"SUPER"
	some 		"SOME"
	global		"GLOBAL"
	tableChecksum	"TABLE_CHECKSUM"
	tables		"TABLES"
	tablespace	"TABLESPACE"
	temporary	"TEMPORARY"
	temptable	"TEMPTABLE"
	textType	"TEXT"
	than		"THAN"
	timeType	"TIME"
	timestampType	"TIMESTAMP"
	trace		"TRACE"
	traditional	"TRADITIONAL"
	transaction	"TRANSACTION"
	triggers	"TRIGGERS"
	truncate	"TRUNCATE"
	tp             	"TYPE"
	unbounded	"UNBOUNDED"
	uncommitted	"UNCOMMITTED"
	unicodeSym	"UNICODE"
	unknown 	"UNKNOWN"
	user		"USER"
	undefined	"UNDEFINED"
	validation	"VALIDATION"
	value		"VALUE"
	variables	"VARIABLES"
	view		"VIEW"
	visible		"VISIBLE"
	binding		"BINDING"
	bindings	"BINDINGS"
	warnings	"WARNINGS"
	without		"WITHOUT"
	identSQLErrors	"ERRORS"
	week		"WEEK"
	yearType	"YEAR"
	x509		"X509"
	enforced	"ENFORCED"
	nowait          "NOWAIT"

	/* The following tokens belong to NotKeywordToken. Notice: make sure these tokens are contained in NotKeywordToken. */
	addDate			"ADDDATE"
	bitAnd			"BIT_AND"
	bitOr			"BIT_OR"
	bitXor			"BIT_XOR"
	bound			"BOUND"
	cast			"CAST"
	copyKwd			"COPY"
	count			"COUNT"
	curTime			"CURTIME"
	dateAdd			"DATE_ADD"
	dateSub			"DATE_SUB"
	exact 	 		"EXACT"
	extract			"EXTRACT"
	flashback		"FLASHBACK"
	getFormat		"GET_FORMAT"
	groupConcat		"GROUP_CONCAT"
	next_row_id		"NEXT_ROW_ID"
	inplace 		"INPLACE"
	instant			"INSTANT"
	internal		"INTERNAL"
	min			"MIN"
	max			"MAX"
	maxExecutionTime	"MAX_EXECUTION_TIME"
	now			"NOW"
	position		"POSITION"
	recent			"RECENT"
	staleness		"STALENESS"
	std			"STD"
	stddev			"STDDEV"
	stddevPop		"STDDEV_POP"
	stddevSamp		"STDDEV_SAMP"
	strong			"STRONG"
	subDate			"SUBDATE"
	sum			"SUM"
	substring		"SUBSTRING"
	timestampAdd		"TIMESTAMPADD"
	timestampDiff		"TIMESTAMPDIFF"
	tokudbDefault	"TOKUDB_DEFAULT"
	tokudbFast	"TOKUDB_FAST"
	tokudbLzma	"TOKUDB_LZMA"
	tokudbQuickLZ	"TOKUDB_QUICKLZ"
	tokudbSnappy	"TOKUDB_SNAPPY"
	tokudbSmall	"TOKUDB_SMALL"
	tokudbUncompressed	"TOKUDB_UNCOMPRESSED"
	tokudbZlib	"TOKUDB_ZLIB"
	top			"TOP"
	trim			"TRIM"
	variance		"VARIANCE"
	varPop			"VAR_POP"
	varSamp			"VAR_SAMP"
	exprPushdownBlacklist		"EXPR_PUSHDOWN_BLACKLIST"
	optRuleBlacklist		"OPT_RULE_BLACKLIST"

	/* The following tokens belong to TiDBKeyword. Notice: make sure these tokens are contained in TiDBKeyword. */
	admin		"ADMIN"
	buckets		"BUCKETS"
	builtins    "BUILTINS"
	cancel		"CANCEL"
	cmSketch	"CMSKETCH"
	ddl		"DDL"
	depth		"DEPTH"
	drainer		"DRAINER"
	jobs		"JOBS"
	job		"JOB"
	nodeID		"NODE_ID"
	nodeState	"NODE_STATE"
	optimistic	"OPTIMISTIC"
	pessimistic	"PESSIMISTIC"
	pump		"PUMP"
	samples		"SAMPLES"
	stats		"STATS"
	statsMeta       "STATS_META"
	statsHistograms "STATS_HISTOGRAMS"
	statsBuckets    "STATS_BUCKETS"
	statsHealthy    "STATS_HEALTHY"
	tidb		"TIDB"
	hintAggToCop	"AGG_TO_COP"
	hintHJ		"HASH_JOIN"
	hintSMJ		"SM_JOIN"
	hintINLJ	"INL_JOIN"
	hintINLHJ	"INL_HASH_JOIN"
	hintINLMJ	"INL_MERGE_JOIN"
	hintSJI	        "SWAP_JOIN_INPUTS"
	hintNSJI        "NO_SWAP_JOIN_INPUTS"
	hintHASHAGG	"HASH_AGG"
	hintSTREAMAGG	"STREAM_AGG"
	hintUseIndex 		"USE_INDEX"
	hintIgnoreIndex 	"IGNORE_INDEX"
	hintUseIndexMerge	"USE_INDEX_MERGE"
	hintNoIndexMerge	"NO_INDEX_MERGE"
	hintUseToja	"USE_TOJA"
	hintEnablePlanCache	"ENABLE_PLAN_CACHE"
	hintUsePlanCache	"USE_PLAN_CACHE"
	hintReadConsistentReplica	"READ_CONSISTENT_REPLICA"
	hintReadFromStorage   "READ_FROM_STORAGE"
	hintQBName	"QB_NAME"
	hintQueryType	"QUERY_TYPE"
	hintMemoryQuota	"MEMORY_QUOTA"
	hintOLAP	"OLAP"
	hintOLTP	"OLTP"
	hintTiKV    "TIKV"
	hintTiFlash "TIFLASH"
	topn		"TOPN"
	split		"SPLIT"
	width		"WIDTH"
	regions         "REGIONS"
	region          "REGION"

	builtinAddDate
	builtinBitAnd
	builtinBitOr
	builtinBitXor
	builtinCast
	builtinCount
	builtinCurDate
	builtinCurTime
	builtinDateAdd
	builtinDateSub
	builtinExtract
	builtinGroupConcat
	builtinMax
	builtinMin
	builtinNow
	builtinPosition
	builtinSubDate
	builtinSubstring
	builtinSum
	builtinSysDate
	builtinStddevPop
	builtinStddevSamp
	builtinTrim
	builtinUser
	builtinVarPop
	builtinVarSamp

%token	<item>

	/*yy:token "1.%d"   */	floatLit        "floating-point literal"
	/*yy:token "1.%d"   */	decLit          "decimal literal"
	/*yy:token "%d"     */	intLit          "integer literal"
	/*yy:token "%x"     */	hexLit          "hexadecimal literal"
	/*yy:token "%b"     */	bitLit          "bit literal"

	andnot		"&^"
	assignmentEq	":="
	eq		"="
	ge		">="
	le		"<="
	jss		"->"
	juss		"->>"
	lsh		"<<"
	neq		"!="
	neqSynonym	"<>"
	nulleq		"<=>"
	rsh		">>"

%token not2

%type	<expr>
	Expression			"expression"
	BoolPri				"boolean primary expression"
	ExprOrDefault			"expression or default"
	PredicateExpr			"Predicate expression factor"
	SetExpr				"Set variable statement value's expression"
	BitExpr				"bit expression"
	SimpleExpr			"simple expression"
	SimpleIdent			"Simple Identifier expression"
	SumExpr				"aggregate functions"
	FunctionCallGeneric		"Function call with Identifier"
	FunctionCallKeyword		"Function call with keyword as function name"
	FunctionCallNonKeyword		"Function call with nonkeyword as function name"
	Literal				"literal value"
	Variable			"User or system variable"
	SystemVariable			"System defined variable name"
	UserVariable			"User defined variable name"
	StringLiteral			"text literal"
	ExpressionOpt			"Optional expression"
	SignedLiteral			"Literal or NumLiteral with sign"
	DefaultValueExpr		"DefaultValueExpr(Now or Signed Literal)"
	NowSymOptionFraction		"NowSym with optional fraction part"
	CharsetNameOrDefault		"Character set name or default"

%type	<statement>
	AdminStmt			"Check table statement or show ddl statement"
	AlterTableStmt			"Alter table statement"
	AnalyzeTableStmt		"Analyze table statement"
	BeginTransactionStmt		"BEGIN TRANSACTION statement"

	CommitStmt			"COMMIT statement"
	CreateTableStmt			"CREATE TABLE statement"
	CreateDatabaseStmt		"Create Database Statement"
	CreateIndexStmt			"CREATE INDEX statement"
	DropDatabaseStmt		"DROP DATABASE statement"
	DropIndexStmt			"DROP INDEX statement"
	DropTableStmt			"DROP TABLE statement"
	DeleteFromStmt			"DELETE FROM statement"
	EmptyStmt			"empty statement"
	ExplainStmt			"EXPLAIN statement"
	ExplainableStmt			"explainable statement"
	InsertIntoStmt			"INSERT INTO statement"
	SelectStmt			"SELECT statement"
	ReplaceIntoStmt			"REPLACE INTO statement"
	RollbackStmt			"ROLLBACK statement"
	SetStmt				"Set variable statement"
	ShowStmt			"Show engines/databases/tables/user/columns/warnings/status statement"
	Statement			"statement"
	TruncateTableStmt		"TRUNCATE TABLE statement"
	UseStmt				"USE statement"

%type   <item>
	AlterTableSpec			"Alter table specification"
	AlterTableSpecList		"Alter table specification list"
	AlterTableSpecListOpt		"Alter table specification list optional"
	AnyOrAll			"Any or All for subquery"
	Assignment			"assignment"
	AssignmentList			"assignment list"
	AssignmentListOpt		"assignment list opt"
	OptionalBraces			"optional braces"
	CastType			"Cast function target type"
	CharsetName			"Character set name"
	CollationName			"Collation name"
	ColumnDef			"table column definition"
	ColumnDefList			"table column definition list"
	ColumnFormat			"Column format"
	ColumnName			"column name"
	ColumnNameList			"column name list"
	ColumnNameListOpt		"column name list opt"
	ColumnSetValue			"insert statement set value by column name"
	ColumnSetValueList		"insert statement set value by column name list"
	CompareOp			"Compare opcode"
	ColumnOption			"column definition option"
	ColumnOptionList		"column definition option list"
	VirtualOrStored			"indicate generated column is stored or not"
	ColumnOptionListOpt		"optional column definition option list"
	Constraint			"table constraint"
	ConstraintElem			"table constraint element"
	ConstraintKeywordOpt		"Constraint Keyword or empty"
	DatabaseOption			"CREATE Database specification"
	DatabaseOptionList		"CREATE Database specification list"
	DatabaseOptionListOpt		"CREATE Database specification list opt"
	DBName				"Database Name"
	DistinctOpt			"Explicit distinct option"
	DefaultFalseDistinctOpt		"Distinct option which defaults to false"
	DefaultTrueDistinctOpt		"Distinct option which defaults to true"
	EqOpt				"= or empty"
	EscapedTableRef 		"escaped table reference"
	ExplainFormatType		"explain format type"
	ExpressionList			"expression list"
	ExpressionListOpt		"expression list opt"
	FuncDatetimePrecListOpt	        "Function datetime precision list opt"
	FuncDatetimePrecList	        "Function datetime precision list"
	Field				"field expression"
	FieldAsName			"Field alias name"
	FieldAsNameOpt			"Field alias name opt"
	FieldList			"field expression list"
	TableRefsClause			"Table references clause"
	FuncDatetimePrec		"Function datetime precision"
	GlobalScope			"The scope of variable"
	GroupByClause			"GROUP BY clause"
	HavingClause			"HAVING clause"
	IfExists			"If Exists"
	IfNotExists			"If Not Exists"
	IndexHint			"index hint"
	IndexHintList			"index hint list"
	IndexHintListOpt		"index hint list opt"
	IndexHintScope			"index hint scope"
	IndexHintType			"index hint type"
	IndexInvisible			"index visible/invisible"
	IndexKeyTypeOpt			"index key type"
	IndexName			"index name"
	IndexNameAndTypeOpt		"index name and index type"
	IndexNameList			"index name list"
	IndexOption			"Index Option"
	IndexOptionList			"Index Option List or empty"
	IndexType			"index type"
	IndexTypeName			"index type name"
	IndexTypeOpt			"optional index type"
	IndexPartSpecification		"Index column name or expression"
	IndexPartSpecificationList	"List of index column name or expression"
	IndexPartSpecificationListOpt   "Optional list of index column name or expression"
	InsertValues			"Rest part of INSERT/REPLACE INTO statement"
	JoinTable 			"join table"
	JoinType			"join type"
	LocationLabelList		"location label name list"
	LikeEscapeOpt 			"like escape option"
	LikeTableWithOrWithoutParen	"LIKE table_name or ( LIKE table_name )"
	LimitClause			"LIMIT clause"
	LimitOption			"Limit option could be integer or parameter marker."
	NumLiteral			"Num/Int/Float/Decimal Literal"
	OptFull				"Full or empty"
	OptTemporary			"TEMPORARY or empty"
	Order				"ORDER BY clause optional collation specification"
	OrderBy				"ORDER BY clause"
	ByItem				"BY item"
	OrderByOptional			"Optional ORDER BY clause optional"
	ByList				"BY list"
	QuickOptional			"QUICK or empty"
	QueryBlockOpt			"Query block identifier optional"
	PriorityOpt			"Statement priority option"
	OptGConcatSeparator		"optional GROUP_CONCAT SEPARATOR"
	RowValue			"Row value"
	SelectStmtCalcFoundRows		"SELECT statement optional SQL_CALC_FOUND_ROWS"
	SelectStmtSQLBigResult		"SELECT statement optional SQL_BIG_RESULT"
	SelectStmtSQLBufferResult	"SELECT statement optional SQL_BUFFER_RESULT"
	SelectStmtSQLCache		"SELECT statement optional SQL_CAHCE/SQL_NO_CACHE"
	SelectStmtSQLSmallResult	"SELECT statement optional SQL_SMALL_RESULT"
	SelectStmtStraightJoin		"SELECT statement optional STRAIGHT_JOIN"
	SelectStmtFieldList		"SELECT statement field list"
	SelectStmtLimit			"SELECT statement optional LIMIT clause"
	SelectStmtOpts			"Select statement options"
	SelectStmtBasic			"SELECT statement from constant value"
	SelectStmtFromDualTable			"SELECT statement from dual table"
	SelectStmtFromTable			"SELECT statement from table"
	SelectStmtGroup			"SELECT statement optional GROUP BY clause"
	ShowTargetFilterable    	"Show target that can be filtered by WHERE or LIKE"
	ShowDatabaseNameOpt		"Show tables/columns statement database name option"
	ShowTableAliasOpt       	"Show table alias option"
	ShowLikeOrWhereOpt		"Show like or where clause option"
	StatementList			"statement list"
	StringName			"string literal or identifier"
	StringList 			"string list"
	Symbol				"Constraint Symbol"
	TableAliasRefList		"table alias reference list"
	TableAsName			"table alias name"
	TableAsNameOpt 			"table alias name optional"
	TableElement			"table definition element"
	TableElementList		"table definition element list"
	TableElementListOpt		"table definition element list optional"
	TableFactor 			"table factor"
	TableName			"Table name"
	TableNameOptWild		"Table name with optional wildcard"
	TableNameList			"Table name list"
	TableNameListOpt		"Table name list opt"
	TableRef 			"table reference"
	TableRefs 			"table references"

	Values			"values"
	ValuesList		"values list"
	ValuesOpt		"values optional"
	VariableAssignment	"set variable value"
	VariableAssignmentList	"set variable value list"
	WhereClause		"WHERE clause"
	WhereClauseOptional	"Optional WHERE clause"
	WithValidation		"with validation"
	WithValidationOpt	"optional with validation"
	Type			"Types"

	OptWild			"Optional Wildcard"

	BetweenOrNotOp		"Between predicate"
	IsOrNotOp		"Is predicate"
	InOrNotOp		"In predicate"

	NumericType		"Numeric types"
	IntegerType		"Integer Types types"
	BooleanType 		"Boolean Types types"
	FixedPointType		"Exact value types"
	FloatingPointType	"Approximate value types"
	BitValueType		"bit value types"
	StringType		"String types"
	BlobType		"Blob types"
	TextType		"Text types"
	DateAndTimeType		"Date and Time types"

	OptFieldLen		"Field length or empty"
	FieldLen		"Field length"
	FieldOpts		"Field type definition option list"
	FieldOpt		"Field type definition option"
	FloatOpt		"Floating-point type option"
	Precision		"Floating-point precision option"
	OptBinary		"Optional BINARY"
	OptBinMod		"Optional BINARY mode"
	OptCharsetWithOptBinary	"Optional BINARY or ASCII or UNICODE or BYTE"
	OptCharset		"Optional Character setting"
	OptCollate		"Optional Collate setting"
	NUM			"A number"
	LengthNum		"Field length num(uint64)"
	StorageOptimizerHintOpt "Storage level optimizer hint"
	TableOptimizerHintOpt	"Table level optimizer hint"
	TableOptimizerHints	"Table level optimizer hints"
	OptimizerHintList	"optimizer hint list"
	HintTable		"Table in optimizer hint"
	HintTableList		"Table list in optimizer hint"
	HintStorageType "storage type in optimizer hint"
	HintStorageTypeAndTable  "storage type and tables in optimizer hint"
	HintStorageTypeAndTableList  "storage type and tables list in optimizer hint"
	HintTrueOrFalse		"True or false in optimizer hint"
	HintQueryType		"Query type in optimizer hint"
	HintMemoryQuota		"Memory quota in optimizer hint"
	EnforcedOrNot		"{ENFORCED|NOT ENFORCED}"
	EnforcedOrNotOpt	"Optional {ENFORCED|NOT ENFORCED}"
	EnforcedOrNotOrNotNullOpt	"{[ENFORCED|NOT ENFORCED|NOT NULL]}"

%type	<ident>
	AsOpt			"AS or EmptyString"
	KeyOrIndex		"{KEY|INDEX}"
	ColumnKeywordOpt	"Column keyword or empty"
	PrimaryOpt		"Optional primary keyword"
	NowSym			"CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP"
	NowSymFunc		"CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP/NOW"
	DefaultKwdOpt		"optional DEFAULT keyword"
	DatabaseSym		"DATABASE or SCHEMA"
	ExplainSym		"EXPLAIN or DESCRIBE or DESC"
	IntoOpt			"INTO or EmptyString"
	ValueSym		"Value or Values"
	Char			"{CHAR|CHARACTER}"
	NChar			"{NCHAR|NATIONAL CHARACTER|NATIONAL CHAR}"
	Varchar			"{VARCHAR|VARCHARACTER|CHARACTER VARYING|CHAR VARYING}"
	NVarchar		"{NATIONAL VARCHAR|NATIONAL VARCHARACTER|NVARCHAR|NCHAR VARCHAR|NATIONAL CHARACTER VARYING|NATIONAL CHAR VARYING|NCHAR VARYING}"
	Year			"{YEAR|SQL_TSI_YEAR}"
	OuterOpt		"optional OUTER clause"
	CrossOpt		"Cross join option"
	ShowIndexKwd		"Show index/indexs/key keyword"
	DistinctKwd		"DISTINCT/DISTINCTROW keyword"
	FromOrIn		"From or In"
	OptTable		"Optional table keyword"
	OptInteger		"Optional Integer keyword"
	CharsetKw		"charset or charater set"
	CommaOpt		"optional comma"
	logAnd			"logical and operator"
	logOr			"logical or operator"
	StorageMedia		"{DISK|MEMORY|DEFAULT}"

%type	<ident>
	Identifier			"identifier or unreserved keyword"
	NotKeywordToken			"Tokens not mysql keyword but treated specially"
	UnReservedKeyword		"MySQL unreserved keywords"
	TiDBKeyword			"TiDB added keywords"
	FunctionNameConflict		"Built-in function call names which are conflict with keywords"
	FunctionNameOptionalBraces	"Function with optional braces, all of them are reserved keywords."
	FunctionNameDatetimePrecision	"Function with optional datetime precision, all of them are reserved keywords."
	FunctionNameDateArith		"Date arith function call names (date_add or date_sub)"
	FunctionNameDateArithMultiForms	"Date arith function call names (adddate or subdate)"
	VariableName			"A simple Identifier like xx or the xx.xx form"

%precedence empty

%precedence sqlBufferResult
%precedence sqlBigResult
%precedence sqlSmallResult
%precedence sqlCache sqlNoCache
%precedence lowerThanIntervalKeyword
%precedence interval
%precedence lowerThanStringLitToken
%precedence stringLit
%precedence lowerThanSetKeyword
%precedence set
%precedence lowerThanInsertValues
%precedence insertValues
%precedence lowerThanCreateTableSelect
%precedence createTableSelect
%precedence lowerThanCharsetKwd
%precedence charsetKwd
%precedence lowerThanKey
%precedence key
%precedence lowerThanLocal
%precedence local
%precedence lowerThanRemove
%precedence remove
%precedence lowerThenOrder
%precedence order

%left   join straightJoin inner cross left right full natural
/* A dummy token to force the priority of TableRef production in a join. */
%left   tableRefPriority
%precedence lowerThanOn
%precedence on using
%right   assignmentEq
%left 	pipes or pipesAsOr
%left 	xor
%left 	andand and
%left 	between
%precedence	lowerThanEq
%left 	eq ge le neq neqSynonym '>' '<' is like in
%left 	'|'
%left 	'&'
%left 	rsh lsh
%left 	'-' '+'
%left 	'*' '/' '%' div mod
%left 	'^'
%left 	'~' neg
%precedence lowerThanNot
%right 	not not2
%right	collate
%right	encryption

%left labels
%precedence '('
%precedence quick
%precedence escape
%precedence lowerThanComma
%precedence ','
%precedence higherThanComma

%start	Start

%%

Start:
	StatementList

/**************************************AlterTableStmt***************************************
 * See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html
 *******************************************************************************************/
AlterTableStmt:
	"ALTER" "TABLE" TableName AlterTableSpecListOpt
	{
		specs := $4.([]*ast.AlterTableSpec)
		$$ = &ast.AlterTableStmt{
			Table: $3.(*ast.TableName),
			Specs: specs,
		}
	}

LocationLabelList:
	{
		$$ = []string{}
	}
|	"LOCATION" "LABELS" StringList
	{
		$$ = $3
	}


AlterTableSpec:
	"ADD" ColumnKeywordOpt IfNotExists ColumnDef
	{
		$$ = &ast.AlterTableSpec{
			IfNotExists: 	$3.(bool),
			Tp: 		ast.AlterTableAddColumns,
			NewColumns:	[]*ast.ColumnDef{$4.(*ast.ColumnDef)},
		}
	}
|	"ADD" ColumnKeywordOpt IfNotExists '(' TableElementList ')'
	{
		tes := $5.([]interface {})
		var columnDefs []*ast.ColumnDef
		var constraints []*ast.Constraint
		for _, te := range tes {
			switch te := te.(type) {
			case *ast.ColumnDef:
				columnDefs = append(columnDefs, te)
			case *ast.Constraint:
				constraints = append(constraints, te)
			}
		}
		$$ = &ast.AlterTableSpec{
			IfNotExists: 	$3.(bool),
			Tp: 		ast.AlterTableAddColumns,
			NewColumns:	columnDefs,
			NewConstraints: constraints,
		}
	}
|	"ADD" Constraint
	{
		constraint := $2.(*ast.Constraint)
		$$ = &ast.AlterTableSpec{
			Tp: ast.AlterTableAddConstraint,
			Constraint: constraint,
		}
	}
|	"DROP" ColumnKeywordOpt IfExists ColumnName RestrictOrCascadeOpt
	{
		$$ = &ast.AlterTableSpec{
			IfExists: $3.(bool),
			Tp: ast.AlterTableDropColumn,
			OldColumnName: $4.(*ast.ColumnName),
		}
	}
|	"DROP" "PRIMARY" "KEY"
	{
		$$ = &ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey}
	}
|	"IMPORT" "TABLESPACE"
    {
        ret := &ast.AlterTableSpec{
            Tp: ast.AlterTableImportTablespace,
        }
        $$ = ret
        yylex.AppendError(yylex.Errorf("The IMPORT TABLESPACE clause is parsed but ignored by all storage engines."))
        parser.lastErrorAsWarn()
    }
|	"DISCARD" "TABLESPACE"
    {
        ret := &ast.AlterTableSpec{
            Tp: ast.AlterTableDiscardTablespace,
        }
        $$ = ret
        yylex.AppendError(yylex.Errorf("The DISCARD TABLESPACE clause is parsed but ignored by all storage engines."))
        parser.lastErrorAsWarn()
    }
|	"DROP" KeyOrIndex IfExists Identifier
	{
		$$ = &ast.AlterTableSpec{
			IfExists: $3.(bool),
			Tp: ast.AlterTableDropIndex,
			Name: $4,
		}
	}
|	"DROP" "FOREIGN" "KEY" IfExists Symbol
	{
		$$ = &ast.AlterTableSpec{
			IfExists: $4.(bool),
			Tp: ast.AlterTableDropForeignKey,
			Name: $5.(string),
		}
	}
|	"DISABLE" "KEYS"
	{
		$$ = &ast.AlterTableSpec{
			Tp: ast.AlterTableDisableKeys,
		}
	}
|	"ENABLE" "KEYS"
	{
		$$ = &ast.AlterTableSpec{
			Tp: ast.AlterTableEnableKeys,
		}
	}
|	"MODIFY" ColumnKeywordOpt IfExists ColumnDef
	{
		$$ = &ast.AlterTableSpec{
			IfExists:	$3.(bool),
			Tp:		ast.AlterTableModifyColumn,
			NewColumns:	[]*ast.ColumnDef{$4.(*ast.ColumnDef)},
		}
	}
|	"CHANGE" ColumnKeywordOpt IfExists ColumnName ColumnDef
	{
		$$ = &ast.AlterTableSpec{
			IfExists:	$3.(bool),
			Tp:    		ast.AlterTableChangeColumn,
			OldColumnName:	$4.(*ast.ColumnName),
			NewColumns:	[]*ast.ColumnDef{$5.(*ast.ColumnDef)},
		}
	}
|	"ALTER" ColumnKeywordOpt ColumnName "SET" "DEFAULT" SignedLiteral
	{
		option := &ast.ColumnOption{Expr: $6}
		colDef := &ast.ColumnDef{
			Name: 	 $3.(*ast.ColumnName),
			Options: []*ast.ColumnOption{option},
		}
		$$ = &ast.AlterTableSpec{
			Tp:		ast.AlterTableAlterColumn,
			NewColumns:	[]*ast.ColumnDef{colDef},
		}
	}
|	"ALTER" ColumnKeywordOpt ColumnName "SET" "DEFAULT" '(' Expression ')'
	{
		option := &ast.ColumnOption{Expr: $7}
		colDef := &ast.ColumnDef{
			Name: 	 $3.(*ast.ColumnName),
			Options: []*ast.ColumnOption{option},
		}
		$$ = &ast.AlterTableSpec{
			Tp:		ast.AlterTableAlterColumn,
			NewColumns:	[]*ast.ColumnDef{colDef},
		}
	}
|	"ALTER" ColumnKeywordOpt ColumnName "DROP" "DEFAULT"
	{
		colDef := &ast.ColumnDef{
			Name: 	 $3.(*ast.ColumnName),
		}
		$$ = &ast.AlterTableSpec{
			Tp:		ast.AlterTableAlterColumn,
			NewColumns:	[]*ast.ColumnDef{colDef},
		}
	}
|	"RENAME" "COLUMN" ColumnName "TO" ColumnName
	{
		$$ = &ast.AlterTableSpec{
			Tp:    	    ast.AlterTableRenameColumn,
			OldColumnName:    $3.(*ast.ColumnName),
			NewColumnName:    $5.(*ast.ColumnName),
		}
	}
|	"RENAME" KeyOrIndex Identifier "TO" Identifier
	{
		$$ = &ast.AlterTableSpec{
			Tp:    	    ast.AlterTableRenameIndex,
			FromKey:    model.NewCIStr($3),
			ToKey:      model.NewCIStr($5),
		}
	}
|	"FORCE"
	{
		// Parse it and ignore it. Just for compatibility.
		$$ = &ast.AlterTableSpec{
			Tp:    		ast.AlterTableForce,
		}
	}
|	"WITH" "VALIDATION"
	{
		// Parse it and ignore it. Just for compatibility.
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableWithValidation,
		}
		yylex.AppendError(yylex.Errorf("The WITH/WITHOUT VALIDATION clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}
|	"WITHOUT" "VALIDATION"
	{
		// Parse it and ignore it. Just for compatibility.
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableWithoutValidation,
		}
		yylex.AppendError(yylex.Errorf("The WITH/WITHOUT VALIDATION clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}
// Added in MySQL 8.0.13, see: https://dev.mysql.com/doc/refman/8.0/en/keywords.html for details
|	"SECONDARY_LOAD"
	{
		// Parse it and ignore it. Just for compatibility.
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableSecondaryLoad,
		}
		yylex.AppendError(yylex.Errorf("The SECONDARY_LOAD clause is parsed but not implement yet."))
		parser.lastErrorAsWarn()
	}
// Added in MySQL 8.0.13, see: https://dev.mysql.com/doc/refman/8.0/en/keywords.html for details
|	"SECONDARY_UNLOAD"
	{
		// Parse it and ignore it. Just for compatibility.
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableSecondaryUnload,
		}
		yylex.AppendError(yylex.Errorf("The SECONDARY_UNLOAD VALIDATION clause is parsed but not implement yet."))
		parser.lastErrorAsWarn()
	}
|	"ALTER" "CHECK" Identifier EnforcedOrNot
	{
		// Parse it and ignore it. Just for compatibility.
		c := &ast.Constraint{
			Name: $3,
			Enforced: $4.(bool),
		}
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableAlterCheck,
			Constraint:       c,
		}
		yylex.AppendError(yylex.Errorf("The ALTER CHECK clause is parsed but not implemented yet."))
		parser.lastErrorAsWarn()
	}
|	"DROP" "CHECK" Identifier
	{
		// Parse it and ignore it. Just for compatibility.
		c := &ast.Constraint{
			Name: $3,
		}
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableDropCheck,
			Constraint:       c,
		}
		yylex.AppendError(yylex.Errorf("The DROP CHECK clause is parsed but not implemented yet."))
		parser.lastErrorAsWarn()
	}
|	"ALTER" "INDEX" Identifier IndexInvisible
	{
		$$ = &ast.AlterTableSpec{
			Tp:               ast.AlterTableIndexInvisible,
			Name:             $3,
			Visibility:       $4.(ast.IndexVisibility),
		}
	}

WithValidationOpt:
	{
		$$ = true
	}
|	WithValidation
	{
		$$ = $1
	}

WithValidation:
	"WITH" "VALIDATION"
	{
		$$ = true
	}
|	"WITHOUT" "VALIDATION"
	{
		$$ = false
	}

KeyOrIndex: "KEY" | "INDEX"


KeyOrIndexOpt:
	{}
|	KeyOrIndex

ColumnKeywordOpt:
	{}
|	"COLUMN"

AlterTableSpecListOpt:
	/* empty */
	{
		$$ = make([]*ast.AlterTableSpec, 0, 1)
	}
|	AlterTableSpecList
	{
		$$ = $1
	}

AlterTableSpecList:
	AlterTableSpec
	{
		$$ = []*ast.AlterTableSpec{$1.(*ast.AlterTableSpec)}
	}
|	AlterTableSpecList ',' AlterTableSpec
	{
		$$ = append($1.([]*ast.AlterTableSpec), $3.(*ast.AlterTableSpec))
	}

ConstraintKeywordOpt:
	{
		$$ = nil
	}
|	"CONSTRAINT"
	{
		$$ = nil
	}
|	"CONSTRAINT" Symbol
	{
		$$ = $2.(string)
	}

Symbol:
	Identifier
	{
		$$ = $1
	}

/*******************************************************************************************/

AnalyzeTableStmt:
	"ANALYZE" "TABLE" TableNameList
	 {
		$$ = &ast.AnalyzeTableStmt{TableNames: $3.([]*ast.TableName)}
	 }

/*******************************************************************************************/
Assignment:
	ColumnName eq ExprOrDefault
	{
		$$ = &ast.Assignment{Column: $1.(*ast.ColumnName), Expr:$3}
	}

AssignmentList:
	Assignment
	{
		$$ = []*ast.Assignment{$1.(*ast.Assignment)}
	}
|	AssignmentList ',' Assignment
	{
		$$ = append($1.([]*ast.Assignment), $3.(*ast.Assignment))
	}

AssignmentListOpt:
	/* EMPTY */
	{
		$$ = []*ast.Assignment{}
	}
|	AssignmentList

BeginTransactionStmt:
	"BEGIN"
	{
		$$ = &ast.BeginStmt{}
	}
|	"START" "TRANSACTION"
	{
		$$ = &ast.BeginStmt{}
	}

ColumnDefList:
	ColumnDef
	{
		$$ = []*ast.ColumnDef{$1.(*ast.ColumnDef)}
	}
|	ColumnDefList ',' ColumnDef
	{
		$$ = append($1.([]*ast.ColumnDef), $3.(*ast.ColumnDef))
	}

ColumnDef:
	ColumnName Type ColumnOptionListOpt
	{
		colDef := &ast.ColumnDef{Name: $1.(*ast.ColumnName), Tp: $2.(*types.FieldType), Options: $3.([]*ast.ColumnOption)}
		if !colDef.Validate() {
			yylex.AppendError(yylex.Errorf("Invalid column definition"))
			return 1
		}
		$$ = colDef
	}
|	ColumnName "SERIAL" ColumnOptionListOpt
	{
		// TODO: check flen 0
		tp := types.NewFieldType(mysql.TypeLonglong)
		options := []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}}
		options = append(options, $3.([]*ast.ColumnOption)...)
		tp.Flag |= mysql.UnsignedFlag
		colDef := &ast.ColumnDef{Name: $1.(*ast.ColumnName), Tp: tp, Options: options}
		if !colDef.Validate() {
			yylex.AppendError(yylex.Errorf("Invalid column definition"))
			return 1
		}
		$$ = colDef
	}

ColumnName:
	Identifier
	{
		$$ = &ast.ColumnName{Name: model.NewCIStr($1)}
	}
|	Identifier '.' Identifier
	{
		$$ = &ast.ColumnName{Table: model.NewCIStr($1), Name: model.NewCIStr($3)}
	}
|	Identifier '.' Identifier '.' Identifier
	{
		$$ = &ast.ColumnName{Schema: model.NewCIStr($1), Table: model.NewCIStr($3), Name: model.NewCIStr($5)}
	}

ColumnNameList:
	ColumnName
	{
		$$ = []*ast.ColumnName{$1.(*ast.ColumnName)}
	}
|	ColumnNameList ',' ColumnName
	{
		$$ = append($1.([]*ast.ColumnName), $3.(*ast.ColumnName))
	}

ColumnNameListOpt:
	/* EMPTY */
	{
		$$ = []*ast.ColumnName{}
	}
|	ColumnNameList
	{
		$$ = $1.([]*ast.ColumnName)
	}

CommitStmt:
	"COMMIT"
	{
		$$ = &ast.CommitStmt{}
	}

PrimaryOpt:
	{}
|	"PRIMARY"

EnforcedOrNot:
	"ENFORCED"
	{
		$$ = true
	}
|	"NOT" "ENFORCED"
	{
		$$ = false
	}

EnforcedOrNotOpt:
	{
		$$ = true
	} %prec lowerThanNot
|	EnforcedOrNot
	{
		$$ = $1
	}

EnforcedOrNotOrNotNullOpt:
//	 This branch is needed to workaround the need of a lookahead of 2 for the grammar:
//
//	  { [NOT] NULL | CHECK(...) [NOT] ENFORCED } ...
	"NOT" "NULL"
	{
		$$ = 0
	}
|	EnforcedOrNotOpt
	{
		if ($1.(bool)) {
			$$ = 1
		} else {
			$$ = 2
		}
	}

ColumnOption:
	"NOT" "NULL"
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionNotNull}
	}
|	"NULL"
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionNull}
	}
|	"AUTO_INCREMENT"
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement}
	}
|	PrimaryOpt "KEY"
	{
		// KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY
		// can also be specified as just KEY when given in a column definition.
		// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey}
	}
|	"UNIQUE" %prec lowerThanKey
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey}
	}
|	"UNIQUE" "KEY"
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey}
	}
|	"DEFAULT" DefaultValueExpr
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionDefaultValue, Expr: $2}
	}
|	"SERIAL" "DEFAULT" "VALUE"
	{
		$$ = []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}}
	}
|	"ON" "UPDATE" NowSymOptionFraction
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionOnUpdate, Expr: $3}
	}
|	"COMMENT" stringLit
	{
		$$ =  &ast.ColumnOption{Tp: ast.ColumnOptionComment, Expr: ast.NewValueExpr($2)}
	}
|	ConstraintKeywordOpt "CHECK" '(' Expression ')' EnforcedOrNotOrNotNullOpt
	{
		// See https://dev.mysql.com/doc/refman/5.7/en/create-table.html
		// The CHECK clause is parsed but ignored by all storage engines.
		// See the branch named `EnforcedOrNotOrNotNullOpt`.

		optionCheck := &ast.ColumnOption{
			Tp: ast.ColumnOptionCheck,
			Expr: $4,
			Enforced: true,
		}
		switch $6.(int) {
		case 0:
			$$ = []*ast.ColumnOption{optionCheck, {Tp: ast.ColumnOptionNotNull}}
		case 1:
			optionCheck.Enforced = true
			$$ = optionCheck
		case 2:
			optionCheck.Enforced = false
			$$ = optionCheck
		default:
		}
		yylex.AppendError(yylex.Errorf("The CHECK clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}
|	GeneratedAlways "AS" '(' Expression ')' VirtualOrStored
	{
		startOffset := parser.startOffset(&yyS[yypt-2])
		endOffset := parser.endOffset(&yyS[yypt-1])
		expr := $4
		expr.SetText(parser.src[startOffset:endOffset])

		$$ = &ast.ColumnOption{
			Tp: ast.ColumnOptionGenerated,
			Expr: expr,
			Stored: $6.(bool),
		}
	}
|	"COLLATE" CollationName
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionCollate, StrValue: $2.(string)}
	}
|	"COLUMN_FORMAT" ColumnFormat
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionColumnFormat, StrValue: $2.(string)}
	}
|	"STORAGE" StorageMedia
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionStorage, StrValue: $2}
		yylex.AppendError(yylex.Errorf("The STORAGE clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}
|	"AUTO_RANDOM" OptFieldLen
	{
		$$ = &ast.ColumnOption{Tp: ast.ColumnOptionAutoRandom, AutoRandomBitLength: $2.(int)}
	}

StorageMedia:
	"DEFAULT" | "DISK" | "MEMORY"

ColumnFormat:
	"DEFAULT"
	{
		$$ = "DEFAULT"
	}
|	"FIXED"
	{
		$$ = "FIXED"
	}
|	"DYNAMIC"
	{
		$$ = "DYNAMIC"
	}

GeneratedAlways: | "GENERATED" "ALWAYS"

VirtualOrStored:
	{
		$$ = false
	}
|	"VIRTUAL"
	{
		$$ = false
	}
|	"STORED"
	{
		$$ = true
	}

ColumnOptionList:
	ColumnOption
	{
		if columnOption,ok := $1.(*ast.ColumnOption); ok {
			$$ = []*ast.ColumnOption{columnOption}
		} else {
			$$ = $1
		}
	}
|	ColumnOptionList ColumnOption
	{
		if columnOption,ok := $2.(*ast.ColumnOption); ok {
			$$ = append($1.([]*ast.ColumnOption), columnOption)
		} else {
			$$ = append($1.([]*ast.ColumnOption), $2.([]*ast.ColumnOption)...)
		}
	}

ColumnOptionListOpt:
	{
		$$ = []*ast.ColumnOption{}
	}
|	ColumnOptionList
	{
		$$ = $1.([]*ast.ColumnOption)
	}

ConstraintElem:
	"PRIMARY" "KEY" IndexNameAndTypeOpt '(' IndexPartSpecificationList ')' IndexOptionList
	{
		c := &ast.Constraint{
			Tp: ast.ConstraintPrimaryKey,
			Keys: $5.([]*ast.IndexPartSpecification),
			Name: $3.([]interface{})[0].(string),
		}
		if $7 != nil {
			c.Option = $7.(*ast.IndexOption)
		}
		if indexType := $3.([]interface{})[1]; indexType != nil {
			if c.Option == nil {
				c.Option = &ast.IndexOption{}
			}
			c.Option.Tp = indexType.(model.IndexType)
		}
		$$ = c
	}
|	"FULLTEXT" KeyOrIndexOpt IndexName '(' IndexPartSpecificationList ')' IndexOptionList
	{
		c := &ast.Constraint{
			Tp:	ast.ConstraintFulltext,
			Keys:	$5.([]*ast.IndexPartSpecification),
			Name:	$3.(string),
		}
		if $7 != nil {
			c.Option = $7.(*ast.IndexOption)
		}
		$$ = c
	}
|	KeyOrIndex IfNotExists IndexNameAndTypeOpt '(' IndexPartSpecificationList ')' IndexOptionList
	{
		c := &ast.Constraint{
			IfNotExists:	$2.(bool),
			Tp:		ast.ConstraintIndex,
			Keys:		$5.([]*ast.IndexPartSpecification),
		}
		if $7 != nil {
			c.Option = $7.(*ast.IndexOption)
		}
		c.Name = $3.([]interface{})[0].(string)
		if indexType := $3.([]interface{})[1]; indexType != nil {
			if c.Option == nil {
				c.Option = &ast.IndexOption{}
			}
			c.Option.Tp = indexType.(model.IndexType)
		}
		$$ = c
	}
|	"UNIQUE" KeyOrIndexOpt IndexNameAndTypeOpt '(' IndexPartSpecificationList ')' IndexOptionList
	{
		c := &ast.Constraint{
			Tp:	ast.ConstraintUniq,
			Keys:	$5.([]*ast.IndexPartSpecification),
		}
		if $7 != nil {
			c.Option = $7.(*ast.IndexOption)
		}
		c.Name = $3.([]interface{})[0].(string)
		if indexType := $3.([]interface{})[1]; indexType != nil {
			if c.Option == nil {
				c.Option = &ast.IndexOption{}
			}
			c.Option.Tp = indexType.(model.IndexType)
		}
		$$ = c
	}
|	"CHECK" '(' Expression ')' EnforcedOrNotOpt
	{
		$$ = &ast.Constraint{
			Tp:		ast.ConstraintCheck,
			Expr:		$3.(ast.ExprNode),
			Enforced:	$5.(bool),
		}
		yylex.AppendError(yylex.Errorf("The CHECK clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}

/*
 * The DEFAULT clause specifies a default value for a column.
 * With one exception, the default value must be a constant;
 * it cannot be a function or an expression. This means, for example,
 * that you cannot set the default for a date column to be the value of
 * a function such as NOW() or CURRENT_DATE. The exception is that you
 * can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP or DATETIME column.
 *
 * See http://dev.mysql.com/doc/refman/5.7/en/create-table.html
 *      https://github.com/mysql/mysql-server/blob/5.7/sql/sql_yacc.yy#L6832
 */
DefaultValueExpr:
	NowSymOptionFraction | SignedLiteral

NowSymOptionFraction:
	NowSym
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")}
	}
|	NowSymFunc '(' ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")}
	}
|	NowSymFunc '(' NUM ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: []ast.ExprNode{ast.NewValueExpr($3)}}
	}

/*
* See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_localtime
* TODO: Process other three keywords
*/
NowSymFunc:
	"CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" | builtinNow
NowSym:
	"CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP"


SignedLiteral:
	Literal
	{
		$$ = ast.NewValueExpr($1)
	}
|	'+' NumLiteral
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: ast.NewValueExpr($2)}
	}
|	'-' NumLiteral
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr($2)}
	}

NumLiteral:
	intLit
|	floatLit
|	decLit

/**************************************CreateIndexStmt***************************************
 * See https://dev.mysql.com/doc/refman/8.0/en/create-index.html
 *
 * TYPE type_name is recognized as a synonym for USING type_name. However, USING is the preferred form.
 *
 * CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
 *     [index_type]
 *     ON tbl_name (key_part,...)
 *     [index_option]
 *     [algorithm_option | lock_option] ...
 *
 * key_part: {col_name [(length)] | (expr)} [ASC | DESC]
 *
 * index_option:
 *     KEY_BLOCK_SIZE [=] value
 *   | index_type
 *   | WITH PARSER parser_name
 *   | COMMENT 'string'
 *   | {VISIBLE | INVISIBLE}
 *
 * index_type:
 *     USING {BTREE | HASH}
 *
 * algorithm_option:
 *     ALGORITHM [=] {DEFAULT | INPLACE | COPY}
 *
 * lock_option:
 *     LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
 *******************************************************************************************/
CreateIndexStmt:
	"CREATE" IndexKeyTypeOpt "INDEX" IfNotExists Identifier IndexTypeOpt "ON" TableName '(' IndexPartSpecificationList ')' IndexOptionList
	{
		var indexOption *ast.IndexOption
		if $12 != nil {
			indexOption = $12.(*ast.IndexOption)
			if indexOption.Tp == model.IndexTypeInvalid {
				if $6 != nil {
					indexOption.Tp = $6.(model.IndexType)
				}
			}
		} else {
			indexOption = &ast.IndexOption{}
			if $6 != nil {
				indexOption.Tp = $6.(model.IndexType)
			}
		}
		$$ = &ast.CreateIndexStmt{
			IfNotExists:   $4.(bool),
			IndexName:     $5,
			Table:         $8.(*ast.TableName),
			IndexPartSpecifications: $10.([]*ast.IndexPartSpecification),
			IndexOption:   indexOption,
			KeyType:       $2.(ast.IndexKeyType),
		}
	}

IndexPartSpecificationListOpt:
	{
		$$ = ([]*ast.IndexPartSpecification)(nil)
	}
|	'(' IndexPartSpecificationList ')'
	{
		$$ = $2
	}

IndexPartSpecificationList:
	IndexPartSpecification
	{
		$$ = []*ast.IndexPartSpecification{$1.(*ast.IndexPartSpecification)}
	}
|	IndexPartSpecificationList ',' IndexPartSpecification
	{
		$$ = append($1.([]*ast.IndexPartSpecification), $3.(*ast.IndexPartSpecification))
	}

IndexPartSpecification:
	ColumnName OptFieldLen Order
	{
		// Order is parsed but just ignored as MySQL did.
		$$ = &ast.IndexPartSpecification{Column: $1.(*ast.ColumnName), Length: $2.(int)}
	}
|	'(' Expression ')' Order
	{
		$$ = &ast.IndexPartSpecification{Expr: $2}
	}

IndexKeyTypeOpt:
	{
		$$ = ast.IndexKeyTypeNone
	}
|	"UNIQUE"
	{
		$$ = ast.IndexKeyTypeUnique
	}
|	"SPATIAL"
	{
		$$ = ast.IndexKeyTypeSpatial
	}
|	"FULLTEXT"
	{
		$$ = ast.IndexKeyTypeFullText
	}

/*******************************************************************
 *
 *  Create Database Statement
 *  CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
 *      [create_specification] ...
 *
 *  create_specification:
 *      [DEFAULT] CHARACTER SET [=] charset_name
 *    | [DEFAULT] COLLATE [=] collation_name
 *    | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
 *******************************************************************/
CreateDatabaseStmt:
	"CREATE" DatabaseSym IfNotExists DBName DatabaseOptionListOpt
	{
		$$ = &ast.CreateDatabaseStmt{
			IfNotExists:	$3.(bool),
			Name:		$4.(string),
			Options:	$5.([]*ast.DatabaseOption),
		}
	}

DBName:
	Identifier
	{
		$$ = $1
	}

DatabaseOption:
	DefaultKwdOpt CharsetKw EqOpt CharsetName
	{
		$$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCharset, Value: $4.(string)}
	}
|	DefaultKwdOpt "COLLATE" EqOpt CollationName
	{
		$$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCollate, Value: $4.(string)}
	}
|	DefaultKwdOpt "ENCRYPTION" EqOpt stringLit
	{
		$$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionEncryption, Value: $4}
	}

DatabaseOptionListOpt:
	{
		$$ = []*ast.DatabaseOption{}
	}
|	DatabaseOptionList

DatabaseOptionList:
	DatabaseOption
	{
		$$ = []*ast.DatabaseOption{$1.(*ast.DatabaseOption)}
	}
|	DatabaseOptionList DatabaseOption
	{
		$$ = append($1.([]*ast.DatabaseOption), $2.(*ast.DatabaseOption))
	}

/*******************************************************************
 *
 *  Create Table Statement
 *
 *  Example:
 *      CREATE TABLE Persons
 *      (
 *          P_Id int NOT NULL,
 *          LastName varchar(255) NOT NULL,
 *          FirstName varchar(255),
 *          Address varchar(255),
 *          City varchar(255),
 *          PRIMARY KEY (P_Id)
 *      )
 *******************************************************************/

CreateTableStmt:
	"CREATE" OptTemporary "TABLE" IfNotExists TableName TableElementListOpt AsOpt
	{
		stmt := $6.(*ast.CreateTableStmt)
		stmt.Table = $5.(*ast.TableName)
		stmt.IfNotExists = $4.(bool)
		stmt.IsTemporary = $2.(bool)
		$$ = stmt
	}
|	"CREATE" OptTemporary "TABLE" IfNotExists TableName LikeTableWithOrWithoutParen
	{
		$$ = &ast.CreateTableStmt{
			Table:          $5.(*ast.TableName),
			ReferTable:	$6.(*ast.TableName),
			IfNotExists:    $4.(bool),
			IsTemporary:    $2.(bool),
		}
	}

DefaultKwdOpt:
	%prec lowerThanCharsetKwd
	{}
|	"DEFAULT"

AsOpt:
	{}
|	"AS"
	{}

LikeTableWithOrWithoutParen:
	"LIKE" TableName
	{
		$$ = $2
	}
|
	'(' "LIKE" TableName ')'
	{
		$$ = $3
	}

/*******************************************************************
 *
 *  Delete Statement
 *
 *******************************************************************/
DeleteFromStmt:
	"DELETE" PriorityOpt QuickOptional "FROM" TableName TableAsNameOpt IndexHintListOpt WhereClauseOptional OrderByOptional LimitClause
	{
		// Single Table
		tn := $5.(*ast.TableName)
		tn.IndexHints = $7.([]*ast.IndexHint)
		join := &ast.Join{Left: &ast.TableSource{Source: tn, AsName: $6.(model.CIStr)}, Right: nil}
		x := &ast.DeleteStmt{
			TableRefs: &ast.TableRefsClause{TableRefs: join},
			Priority:  $2.(mysql.PriorityEnum),
			Quick:	   $3.(bool),
		}
		if $8 != nil {
			x.Where = $8.(ast.ExprNode)
		}
		if $9 != nil {
			x.Order = $9.(*ast.OrderByClause)
		}
		if $10 != nil {
			x.Limit = $10.(*ast.Limit)
		}

		$$ = x
	}

DatabaseSym:
"DATABASE"

DropDatabaseStmt:
	"DROP" DatabaseSym IfExists DBName
	{
		$$ = &ast.DropDatabaseStmt{IfExists: $3.(bool), Name: $4.(string)}
	}

/******************************************************************
 * Drop Index Statement
 * See https://dev.mysql.com/doc/refman/8.0/en/drop-index.html
 *
 *  DROP INDEX index_name ON tbl_name
 *      [algorithm_option | lock_option] ...
 *
 *  algorithm_option:
 *      ALGORITHM [=] {DEFAULT|INPLACE|COPY}
 *
 *  lock_option:
 *      LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}
 ******************************************************************/
DropIndexStmt:
	"DROP" "INDEX" IfExists Identifier "ON" TableName
	{
		$$ = &ast.DropIndexStmt{IfExists: $3.(bool), IndexName: $4, Table: $6.(*ast.TableName)}
	}

DropTableStmt:
	"DROP" OptTemporary TableOrTables IfExists TableNameList RestrictOrCascadeOpt
	{
		$$ = &ast.DropTableStmt{IfExists: $4.(bool), Tables: $5.([]*ast.TableName), IsView: false, IsTemporary: $2.(bool)}
	}

OptTemporary:
	  /* empty */ { $$ = false; }
	| "TEMPORARY" 
	{ 
		$$ = true
		yylex.AppendError(yylex.Errorf("TiDB doesn't support TEMPORARY TABLE, TEMPORARY will be parsed but ignored."))
		parser.lastErrorAsWarn()
	}

RestrictOrCascadeOpt:
	{}
|	"RESTRICT"
|	"CASCADE"

TableOrTables:
	"TABLE"
|	"TABLES"

EqOpt:
	{}
|	eq

EmptyStmt:
	/* EMPTY */
	{
		$$ = nil
	}

ExplainSym:
"EXPLAIN" | "DESCRIBE" | "DESC"

ExplainStmt:
	ExplainSym ExplainableStmt
	{
		$$ = &ast.ExplainStmt{
			Stmt:	$2,
			Format: "row",
		}
	}
|	ExplainSym "FORMAT" "=" stringLit ExplainableStmt
	{
		$$ = &ast.ExplainStmt{
			Stmt:	$5,
			Format: $4,
		}
	}
|	ExplainSym "FORMAT" "=" ExplainFormatType ExplainableStmt
	{
		$$ = &ast.ExplainStmt{
			Stmt:	$5,
			Format: $4.(string),
		}
	}

ExplainFormatType:
	"TRADITIONAL"
	{
		$$ = "row"
	}
|	"JSON"
	{
		$$ = "json"
	}

LengthNum:
	NUM
	{
		$$ = getUint64FromNUM($1)
	}

NUM:
	intLit

Expression:
	singleAtIdentifier assignmentEq Expression %prec assignmentEq
	{
		v := $1
		v = strings.TrimPrefix(v, "@")
		$$ = &ast.VariableExpr{
				Name: 	  v,
				IsGlobal: false,
				IsSystem: false,
				Value:	  $3,
		}
	}
|	Expression logOr Expression %prec pipes
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.LogicOr, L: $1, R: $3}
	}
|	Expression "XOR" Expression %prec xor
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.LogicXor, L: $1, R: $3}
	}
|	Expression logAnd Expression %prec andand
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.LogicAnd, L: $1, R: $3}
	}
|	"NOT" Expression %prec not
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2}
	}
|	BoolPri IsOrNotOp "UNKNOWN" %prec is
	{
		/* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */
		$$ = &ast.IsNullExpr{Expr: $1, Not: !$2.(bool)}
	}
|	BoolPri

logOr:
	pipesAsOr
|	"OR"

logAnd:
"&&" | "AND"

ExpressionList:
	Expression
	{
		$$ = []ast.ExprNode{$1}
	}
|	ExpressionList ',' Expression
	{
		$$ = append($1.([]ast.ExprNode), $3)
	}

ExpressionListOpt:
	{
		$$ = []ast.ExprNode{}
	}
|	ExpressionList

FuncDatetimePrecListOpt:
	{
		$$ = []ast.ExprNode{}
	}
|	FuncDatetimePrecList
	{
		$$ = $1
	}

FuncDatetimePrecList:
	intLit
	{
		expr := ast.NewValueExpr($1)
		$$ = []ast.ExprNode{expr}
	}

BoolPri:
	BoolPri IsOrNotOp "NULL" %prec is
	{
		$$ = &ast.IsNullExpr{Expr: $1, Not: !$2.(bool)}
	}
|	BoolPri CompareOp PredicateExpr %prec eq
	{
		$$ = &ast.BinaryOperationExpr{Op: $2.(opcode.Op), L: $1, R: $3}
	}
|	BoolPri CompareOp singleAtIdentifier assignmentEq PredicateExpr %prec assignmentEq
	{
		v := $3
		v = strings.TrimPrefix(v, "@")
		variable := &ast.VariableExpr{
				Name: 	  v,
				IsGlobal: false,
				IsSystem: false,
				Value:	  $5,
		}
		$$ = &ast.BinaryOperationExpr{Op: $2.(opcode.Op), L: $1, R: variable}
	}
|	PredicateExpr

CompareOp:
	">="
	{
		$$ = opcode.GE
	}
|	'>'
	{
		$$ = opcode.GT
	}
|	"<="
	{
		$$ = opcode.LE
	}
|	'<'
	{
		$$ = opcode.LT
	}
|	"!="
	{
		$$ = opcode.NE
	}
|	"<>"
	{
		$$ = opcode.NE
	}
|	"="
	{
		$$ = opcode.EQ
	}
|	"<=>"
	{
		$$ = opcode.NullEQ
	}

BetweenOrNotOp:
	"BETWEEN"
	{
		$$ = true
	}
|	"NOT" "BETWEEN"
	{
		$$ = false
	}

IsOrNotOp:
	"IS"
	{
		$$ = true
	}
|	"IS" "NOT"
	{
		$$ = false
	}

InOrNotOp:
	"IN"
	{
		$$ = true
	}
|	"NOT" "IN"
	{
		$$ = false
	}

AnyOrAll:
	"ANY"
	{
		$$ = false
	}
|	"SOME"
	{
		$$ = false
	}
|	"ALL"
	{
		$$ = true
	}

PredicateExpr:
	BitExpr InOrNotOp '(' ExpressionList ')'
	{
		$$ = &ast.PatternInExpr{Expr: $1, Not: !$2.(bool), List: $4.([]ast.ExprNode)}
	}
|	BitExpr BetweenOrNotOp BitExpr "AND" PredicateExpr
	{
		$$ = &ast.BetweenExpr{
			Expr:	$1,
			Left:	$3,
			Right:	$5,
			Not:	!$2.(bool),
		}
	}
|	BitExpr

LikeEscapeOpt:
	%prec empty
	{
		$$ = "\\"
	}
|	"ESCAPE" stringLit
	{
		$$ = $2
	}

Field:
	'*'
	{
		$$ = &ast.SelectField{WildCard: &ast.WildCardField{}}
	}
|	Identifier '.' '*'
	{
		wildCard := &ast.WildCardField{Table: model.NewCIStr($1)}
		$$ = &ast.SelectField{WildCard: wildCard}
	}
|	Identifier '.' Identifier '.' '*'
	{
		wildCard := &ast.WildCardField{Schema: model.NewCIStr($1), Table: model.NewCIStr($3)}
		$$ = &ast.SelectField{WildCard: wildCard}
	}
|	Expression FieldAsNameOpt
	{
		expr := $1
		asName := $2.(string)
		$$ = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)}
	}
|	'{' Identifier Expression '}' FieldAsNameOpt
	{
		/*
		* ODBC escape syntax.
		* See https://dev.mysql.com/doc/refman/5.7/en/expressions.html
		*/
		expr := $3
		asName := $5.(string)
		$$ = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)}
	}

FieldAsNameOpt:
	/* EMPTY */
	{
		$$ = ""
	}
|	FieldAsName
	{
		$$ = $1
	}

FieldAsName:
	Identifier
	{
		$$ = $1
	}
|	"AS" Identifier
	{
		$$ = $2
	}
|	stringLit
	{
		$$ = $1
	}
|	"AS" stringLit
	{
		$$ = $2
	}

FieldList:
	Field
	{
		field := $1.(*ast.SelectField)
		field.Offset = parser.startOffset(&yyS[yypt])
		$$ = []*ast.SelectField{field}
	}
|	FieldList ',' Field
	{

		fl := $1.([]*ast.SelectField)
		last := fl[len(fl)-1]
		if last.Expr != nil && last.AsName.O == "" {
			lastEnd := parser.endOffset(&yyS[yypt-1])
			last.SetText(parser.src[last.Offset:lastEnd])
		}
		newField := $3.(*ast.SelectField)
		newField.Offset = parser.startOffset(&yyS[yypt])
		$$ = append(fl, newField)
	}

GroupByClause:
	"GROUP" "BY" ByList
	{
		$$ = &ast.GroupByClause{Items: $3.([]*ast.ByItem)}
	}

HavingClause:
	{
		$$ = nil
	}
|	"HAVING" Expression
	{
		$$ = &ast.HavingClause{Expr: $2}
	}

IfExists:
	{
		$$ = false
	}
|	"IF" "EXISTS"
	{
		$$ = true
	}

IfNotExists:
	{
		$$ = false
	}
|	"IF" "NOT" "EXISTS"
	{
		$$ = true
	}

IndexName:
	{
		$$ = ""
	}
|	Identifier
	{
		//"index name"
		$$ = $1
	}

IndexOptionList:
	{
		$$ = nil
	}
|	IndexOptionList IndexOption
	{
		// Merge the options
		if $1 == nil {
			$$ = $2
		} else {
			opt1 := $1.(*ast.IndexOption)
			opt2 := $2.(*ast.IndexOption)
			if len(opt2.Comment) > 0 {
				opt1.Comment = opt2.Comment
			} else if opt2.Tp != 0 {
				opt1.Tp = opt2.Tp
			} else if opt2.KeyBlockSize > 0 {
			   	opt1.KeyBlockSize = opt2.KeyBlockSize
			} else if len(opt2.ParserName.O) > 0 {
			   	opt1.ParserName = opt2.ParserName
			} else if opt2.Visibility != ast.IndexVisibilityDefault {
				opt1.Visibility = opt2.Visibility
			}
			$$ = opt1
		}
	}

IndexOption:
	"KEY_BLOCK_SIZE" EqOpt LengthNum
	{
		$$ = &ast.IndexOption{
			KeyBlockSize: $3.(uint64),
		}
	}
|	IndexType
	{
		$$ = &ast.IndexOption {
			Tp: $1.(model.IndexType),
		}
	}
|	"WITH" "PARSER" Identifier
	{
		$$ = &ast.IndexOption {
			ParserName: model.NewCIStr($3),
		}
		yylex.AppendError(yylex.Errorf("The WITH PARASER clause is parsed but ignored by all storage engines."))
		parser.lastErrorAsWarn()
	}
|	"COMMENT" stringLit
	{
		$$ = &ast.IndexOption {
			Comment: $2,
		}
	}
|	IndexInvisible
	{
		$$ = &ast.IndexOption {
			Visibility: $1.(ast.IndexVisibility),
		}
	}

/*
  See: https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L7179

  The syntax for defining an index is:

    ... INDEX [index_name] [USING|TYPE] <index_type> ...

  The problem is that whereas USING is a reserved word, TYPE is not. We can
  still handle it if an index name is supplied, i.e.:

    ... INDEX type TYPE <index_type> ...

  here the index's name is unmbiguously 'type', but for this:

    ... INDEX TYPE <index_type> ...

  it's impossible to know what this actually mean - is 'type' the name or the
  type? For this reason we accept the TYPE syntax only if a name is supplied.
*/
IndexNameAndTypeOpt:
	IndexName
	{
		$$ = []interface{}{$1, nil}
	}
| 	IndexName "USING" IndexTypeName
	{
		$$ = []interface{}{$1, $3}
	}
|	Identifier "TYPE" IndexTypeName
	{
		$$ = []interface{}{$1, $3}
	}

IndexTypeOpt:
	{
		$$ = nil
	}
|	IndexType
	{
		$$ = $1
	}

IndexType:
	"USING" IndexTypeName
	{
		$$ = $2
	}
| 	"TYPE" IndexTypeName
	{
		$$ = $2
	}

IndexTypeName:
	"BTREE"
 	{
 		$$ = model.IndexTypeBtree
 	}
 |	"HASH"
 	{
 		$$ = model.IndexTypeHash
 	}
 |	"RTREE"
 	{
 		$$ = model.IndexTypeRtree
 	}

IndexInvisible:
	"VISIBLE"
	{
		$$ = ast.IndexVisibilityVisible
	}
|	"INVISIBLE"
	{
		$$ = ast.IndexVisibilityInvisible
	}

/**********************************Identifier********************************************/
Identifier:
identifier | UnReservedKeyword | NotKeywordToken | TiDBKeyword

UnReservedKeyword:
 "ACTION" | "ADVISE" |"ASCII" | "AUTO_INCREMENT" | "AFTER" | "ALWAYS" | "AVG" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "BTREE" | "BYTE" | "CAPTURE" |"CLEANUP" | "CHARSET"
| "COLUMNS" | "COMMIT" | "COMPACT" | "COMPRESSED" | "CONSISTENT" | "CURRENT" | "DATA" | "DATE" %prec lowerThanStringLitToken| "DATETIME" | "DAY" | "DEALLOCATE" | "DO" | "DUPLICATE"
| "DYNAMIC" | "ENCRYPTION" | "END" | "ENFORCED" | "ENGINE" | "ENGINES" | "ENUM" | "ERRORS" | "ESCAPE" | "EVOLVE" | "EXECUTE" | "EXTENDED" | "FIELDS" | "FIRST" | "FIXED" | "FLUSH" | "FOLLOWING" | "FORMAT" | "FULL" |"GLOBAL"
| "HASH" | "HOUR" | "INSERT_METHOD" | "LESS" | "LOCAL" | "LAST" | "NAMES" | "OFFSET" | "PASSWORD" %prec lowerThanEq | "PREPARE" | "QUICK" | "REBUILD" | "REDUNDANT" | "REORGANIZE"
| "ROLE" |"ROLLBACK" | "SESSION" | "SIGNED" | "SHUTDOWN" | "SNAPSHOT" | "START" | "STATUS" | "OPEN"| "SUBPARTITIONS" | "SUBPARTITION" | "TABLES" | "TABLESPACE" | "TEXT" | "THAN" | "TIME" %prec lowerThanStringLitToken
| "TIMESTAMP" %prec lowerThanStringLitToken | "TRACE" | "TRANSACTION" | "TRUNCATE" | "UNBOUNDED" | "UNKNOWN" | "VALUE" | "WARNINGS" | "YEAR" | "MODE"  | "WEEK"  | "ANY" | "SOME" | "USER" | "IDENTIFIED"
| "COLLATION" | "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MASTER" | "MAX_ROWS"
| "MIN_ROWS" | "NATIONAL" | "NCHAR" | "ROW_FORMAT" | "QUARTER" | "GRANTS" | "TRIGGERS" | "DELAY_KEY_WRITE" | "ISOLATION" | "JSON"
| "REPEATABLE" | "RESPECT" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIAL" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" | "SQL_CACHE" | "INDEXES" | "PROCESSLIST"
| "SQL_NO_CACHE" | "DISABLE"  | "ENABLE" | "REVERSE" | "PRIVILEGES" | "NO" | "BINLOG" | "FUNCTION" | "VIEW" | "BINDING" | "BINDINGS" | "MODIFY" | "EVENTS" | "PARTITIONS"
| "NONE" | "NULLS" | "SUPER" | "EXCLUSIVE" | "STATS_PERSISTENT" | "STATS_AUTO_RECALC" | "ROW_COUNT" | "COALESCE" | "MONTH" | "PROCESS" | "PROFILE" | "PROFILES"
| "MICROSECOND" | "MINUTE" | "PLUGINS" | "PRECEDING" | "QUERY" | "QUERIES" | "SECOND" | "SEPARATOR" | "SHARE" | "SHARED" | "SLOW" | "MAX_CONNECTIONS_PER_HOUR" | "MAX_QUERIES_PER_HOUR" | "MAX_UPDATES_PER_HOUR"
| "MAX_USER_CONNECTIONS" | "REPLICATION" | "CLIENT" | "SLAVE" | "RELOAD" | "TEMPORARY" | "ROUTINE" | "EVENT" | "ALGORITHM" | "DEFINER" | "INVOKER" | "MERGE" | "TEMPTABLE" | "UNDEFINED" | "SECURITY" | "CASCADED"
| "RECOVER" | "CIPHER" | "SUBJECT" | "ISSUER" | "X509" | "NEVER" | "EXPIRE" | "ACCOUNT" | "INCREMENTAL" | "CPU" | "MEMORY" | "BLOCK" | "IO" | "CONTEXT" | "SWITCHES" | "PAGE" | "FAULTS" | "IPC" | "SWAPS" | "SOURCE"
| "TRADITIONAL" | "SQL_BUFFER_RESULT" | "DIRECTORY" | "HISTORY" | "LIST" | "NODEGROUP" | "SYSTEM_TIME" | "PARTIAL" | "SIMPLE" | "REMOVE" | "PARTITIONING" | "STORAGE" | "DISK" | "STATS_SAMPLE_PAGES" | "SECONDARY_ENGINE" | "SECONDARY_LOAD" | "SECONDARY_UNLOAD" | "VALIDATION"
| "WITHOUT" | "RTREE" | "EXCHANGE" | "COLUMN_FORMAT" | "REPAIR" | "IMPORT" | "DISCARD" | "TABLE_CHECKSUM" | "UNICODE" | "AUTO_RANDOM"
| "SQL_TSI_DAY" | "SQL_TSI_HOUR" | "SQL_TSI_MINUTE" | "SQL_TSI_MONTH" | "SQL_TSI_QUARTER" | "SQL_TSI_SECOND" |
"SQL_TSI_WEEK" | "SQL_TSI_YEAR" | "INVISIBLE" | "VISIBLE" | "TYPE" | "NOWAIT" | "REPLICA" | "LOCATION" | "LABELS"
| "LOGS" | "HOSTS" | "AGAINST" | "EXPANSION" | "INCREMENT" | "MINVALUE" | "NOMAXVALUE" | "NOMINVALUE" | "NOCACHE" | "CACHE" | "CYCLE" | "NOCYCLE" | "NOORDER" | "SEQUENCE" | "MAX_MINUTES" | "MAX_IDXNUM" | "PER_TABLE" | "PER_DB"

TiDBKeyword:
 "ADMIN" | "AGG_TO_COP" |"BUCKETS" | "BUILTINS" | "CANCEL" | "CMSKETCH" | "DDL" | "DEPTH" | "DRAINER" | "JOBS" | "JOB" | "NODE_ID" | "NODE_STATE" | "PUMP" | "SAMPLES" | "STATS" | "STATS_META" | "STATS_HISTOGRAMS" | "STATS_BUCKETS" | "STATS_HEALTHY" | "TIDB"
| "HASH_JOIN" | "SM_JOIN" | "INL_JOIN" | "INL_HASH_JOIN"| "INL_MERGE_JOIN" | "SWAP_JOIN_INPUTS" | "NO_SWAP_JOIN_INPUTS" | "HASH_AGG" | "STREAM_AGG" | "USE_INDEX" | "IGNORE_INDEX" | "USE_INDEX_MERGE" | "NO_INDEX_MERGE" | "USE_TOJA" | "ENABLE_PLAN_CACHE" | "USE_PLAN_CACHE"
| "READ_CONSISTENT_REPLICA" | "READ_FROM_STORAGE" | "QB_NAME" | "QUERY_TYPE" | "MEMORY_QUOTA" | "OLAP" | "OLTP" | "TOPN" | "TIKV" | "TIFLASH" | "SPLIT" | "OPTIMISTIC" | "PESSIMISTIC" | "WIDTH" | "REGIONS" | "REGION"

NotKeywordToken:
 "ADDDATE" | "BIT_AND" | "BIT_OR" | "BIT_XOR" | "CAST" | "COPY" | "COUNT" | "CURTIME" | "DATE_ADD" | "DATE_SUB" | "EXTRACT" | "GET_FORMAT" | "GROUP_CONCAT"
| "INPLACE" | "INSTANT" | "INTERNAL" |"MIN" | "MAX" | "MAX_EXECUTION_TIME" | "NOW" | "RECENT" | "POSITION" | "SUBDATE" | "SUBSTRING" | "SUM"
| "STD" | "STDDEV" | "STDDEV_POP" | "STDDEV_SAMP" | "VARIANCE" | "VAR_POP" | "VAR_SAMP"
| "TIMESTAMPADD" | "TIMESTAMPDIFF" | "TOKUDB_DEFAULT" | "TOKUDB_FAST" | "TOKUDB_LZMA" | "TOKUDB_QUICKLZ" | "TOKUDB_SNAPPY" | "TOKUDB_SMALL" | "TOKUDB_UNCOMPRESSED" | "TOKUDB_ZLIB" | "TOP" | "TRIM" | "NEXT_ROW_ID"
| "EXPR_PUSHDOWN_BLACKLIST" | "OPT_RULE_BLACKLIST" | "BOUND" | "EXACT" | "STALENESS" | "STRONG" | "FLASHBACK"

/************************************************************************************
 *
 *  Insert Statements
 *
 **********************************************************************************/
InsertIntoStmt:
	"INSERT" PriorityOpt IntoOpt TableName InsertValues
	{
		x := $5.(*ast.InsertStmt)
		x.Priority = $2.(mysql.PriorityEnum)
		// Wraps many layers here so that it can be processed the same way as select statement.
		ts := &ast.TableSource{Source: $4.(*ast.TableName)}
		x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}}
		$$ = x
	}

IntoOpt:
	{}
|	"INTO"

InsertValues:
	'(' ColumnNameListOpt ')' ValueSym ValuesList
	{
		$$ = &ast.InsertStmt{
			Columns:   $2.([]*ast.ColumnName),
			Lists:      $5.([][]ast.ExprNode),
		}
	}
|	'(' ColumnNameListOpt ')' SelectStmt
	{
		$$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $4.(*ast.SelectStmt)}
	}
|	'(' ColumnNameListOpt ')' '(' SelectStmt ')'
	{
		$$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $5.(*ast.SelectStmt)}
	}
|	ValueSym ValuesList %prec insertValues
	{
		$$ = &ast.InsertStmt{Lists:  $2.([][]ast.ExprNode)}
	}
|	'(' SelectStmt ')'
	{
		$$ = &ast.InsertStmt{Select: $2.(*ast.SelectStmt)}
	}
|	SelectStmt
	{
		$$ = &ast.InsertStmt{Select: $1.(*ast.SelectStmt)}
	}
|	"SET" ColumnSetValueList
	{
		$$ = &ast.InsertStmt{Setlist: $2.([]*ast.Assignment)}
	}

ValueSym:
"VALUE" | "VALUES"

ValuesList:
	RowValue
	{
		$$ = [][]ast.ExprNode{$1.([]ast.ExprNode)}
	}
|	ValuesList ',' RowValue
	{
		$$ = append($1.([][]ast.ExprNode), $3.([]ast.ExprNode))
	}

RowValue:
	'(' ValuesOpt ')'
	{
		$$ = $2
	}

ValuesOpt:
	{
		$$ = []ast.ExprNode{}
	}
|	Values

Values:
	Values ',' ExprOrDefault
	{
		$$ = append($1.([]ast.ExprNode), $3)
	}
|	ExprOrDefault
	{
		$$ = []ast.ExprNode{$1}
	}

ExprOrDefault:
	Expression
|	"DEFAULT"
	{
		$$ = &ast.DefaultExpr{}
	}

ColumnSetValue:
	ColumnName eq ExprOrDefault
	{
		$$ = &ast.Assignment{
			Column:	$1.(*ast.ColumnName),
			Expr:	$3,
		}
	}

ColumnSetValueList:
	{
		$$ = []*ast.Assignment{}
	}
|	ColumnSetValue
	{
		$$ = []*ast.Assignment{$1.(*ast.Assignment)}
	}
|	ColumnSetValueList ',' ColumnSetValue
	{
		$$ = append($1.([]*ast.Assignment), $3.(*ast.Assignment))
	}

/***********************************Insert Statements END************************************/

/************************************************************************************
 *  Replace Statements
 *  See https://dev.mysql.com/doc/refman/5.7/en/replace.html
 *
 *  TODO: support PARTITION
 **********************************************************************************/
ReplaceIntoStmt:
	"REPLACE" PriorityOpt IntoOpt TableName InsertValues
	{
		x := $5.(*ast.InsertStmt)
		x.IsReplace = true
		x.Priority = $2.(mysql.PriorityEnum)
		ts := &ast.TableSource{Source: $4.(*ast.TableName)}
		x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}}
		$$ = x
	}

/***********************************Replace Statements END************************************/

Literal:
	"FALSE"
	{
		$$ = ast.NewValueExpr(false)
	}
|	"NULL"
	{
		$$ = ast.NewValueExpr(nil)
	}
|	"TRUE"
	{
		$$ = ast.NewValueExpr(true)
	}
|	floatLit
	{
		$$ = ast.NewValueExpr($1)
	}
|	decLit
	{
		$$ = ast.NewValueExpr($1)
	}
|	intLit
	{
		$$ = ast.NewValueExpr($1)
	}
|	StringLiteral %prec lowerThanStringLitToken
	{
		$$ = $1
	}
|	"UNDERSCORE_CHARSET" stringLit
	{
		// See https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html
		co, err := charset.GetDefaultCollation($1)
		if err != nil {
			yylex.AppendError(yylex.Errorf("Get collation error for charset: %s", $1))
			return 1
		}
		expr := ast.NewValueExpr($2)
		tp := expr.GetType()
		tp.Charset = $1
		tp.Collate = co
		if tp.Collate == charset.CollationBin {
			tp.Flag |= mysql.BinaryFlag
		}
		$$ = expr
	}
|	hexLit
	{
		$$ = ast.NewValueExpr($1)
	}
|	bitLit
	{
		$$ = ast.NewValueExpr($1)
	}

StringLiteral:
	stringLit
	{
		expr := ast.NewValueExpr($1)
		$$ = expr
	}
|	StringLiteral stringLit
	{
		valExpr := $1.(ast.ValueExpr)
		strLit := valExpr.GetString()
		expr := ast.NewValueExpr(strLit+$2)
		// Fix #4239, use first string literal as projection name.
		if valExpr.GetProjectionOffset() >= 0 {
			expr.SetProjectionOffset(valExpr.GetProjectionOffset())
		} else {
			expr.SetProjectionOffset(len(strLit))
		}
		$$ = expr
	}

OrderBy:
	"ORDER" "BY" ByList
	{
		$$ = &ast.OrderByClause{Items: $3.([]*ast.ByItem)}
	}

ByList:
	ByItem
	{
		$$ = []*ast.ByItem{$1.(*ast.ByItem)}
	}
|	ByList ',' ByItem
	{
		$$ = append($1.([]*ast.ByItem), $3.(*ast.ByItem))
	}

ByItem:
	Expression Order
	{
		$$ = &ast.ByItem{Expr: $1, Desc: $2.(bool)}
	}

Order:
	/* EMPTY */
	{
		$$ = false // ASC by default
	}
|	"ASC"
	{
		$$ = false
	}
|	"DESC"
	{
		$$ = true
	}

OrderByOptional:
	{
		$$ = nil
	}
|	OrderBy
	{
		$$ = $1
	}

BitExpr:
	BitExpr '|' BitExpr %prec '|'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Or, L: $1, R: $3}
	}
|	BitExpr '&' BitExpr %prec '&'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.And, L: $1, R: $3}
	}
|	BitExpr "<<" BitExpr %prec lsh
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.LeftShift, L: $1, R: $3}
	}
|	BitExpr ">>" BitExpr %prec rsh
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.RightShift, L: $1, R: $3}
	}
|	BitExpr '+' BitExpr %prec '+'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Plus, L: $1, R: $3}
	}
|	BitExpr '-' BitExpr %prec '-'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Minus, L: $1, R: $3}
	}
|	BitExpr '*' BitExpr %prec '*'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Mul, L: $1, R: $3}
	}
|	BitExpr '/' BitExpr %prec '/'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Div, L: $1, R: $3}
	}
|	BitExpr '%' BitExpr %prec '%'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1, R: $3}
	}
|	BitExpr "DIV" BitExpr %prec div
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.IntDiv, L: $1, R: $3}
	}
|	BitExpr "MOD" BitExpr %prec mod
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1, R: $3}
	}
|	BitExpr '^' BitExpr
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Xor, L: $1, R: $3}
	}
|	SimpleExpr

SimpleIdent:
	Identifier
	{
		$$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{
			Name: model.NewCIStr($1),
		}}
	}
|	Identifier '.' Identifier
	{
		$$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{
			Table: model.NewCIStr($1),
			Name: model.NewCIStr($3),
		}}
	}
|	'.' Identifier '.' Identifier
	{
		$$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{
			Table: model.NewCIStr($2),
			Name: model.NewCIStr($4),
		}}
	}
|	Identifier '.' Identifier '.' Identifier
	{
		$$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{
			Schema: model.NewCIStr($1),
			Table: model.NewCIStr($3),
			Name: model.NewCIStr($5),
		}}
	}

SimpleExpr:
	SimpleIdent
|	FunctionCallKeyword
|	FunctionCallNonKeyword
|	FunctionCallGeneric
|	SimpleExpr "COLLATE" StringName %prec neg
	{
		// TODO: Create a builtin function hold expr and collation. When do evaluation, convert expr result using the collation.
		$$ = $1
	}
|	Literal
|	Variable
|	SumExpr
|	'!' SimpleExpr %prec neg
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2}
	}
|	'~'  SimpleExpr %prec neg
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.BitNeg, V: $2}
	}
|	'-' SimpleExpr %prec neg
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Minus, V: $2}
	}
|	'+' SimpleExpr %prec neg
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: $2}
	}
|	not2 SimpleExpr %prec neg
	{
		$$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2}
	}
|	'(' Expression ')' {
		startOffset := parser.startOffset(&yyS[yypt-1])
		endOffset := parser.endOffset(&yyS[yypt])
		expr := $2
		expr.SetText(parser.src[startOffset:endOffset])
		$$ = &ast.ParenthesesExpr{Expr: expr}
	}
|	'(' ExpressionList ',' Expression ')'
	{
		values := append($2.([]ast.ExprNode), $4)
		$$ = &ast.RowExpr{Values: values}
	}
|	"ROW" '(' ExpressionList ',' Expression ')'
	{
		values := append($3.([]ast.ExprNode), $5)
		$$ = &ast.RowExpr{Values: values}
	}
|	"CONVERT" '(' Expression "USING" CharsetName ')'
	{
		// See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
		charset1 := ast.NewValueExpr($5)
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3, charset1},
		}
	}
|	"DEFAULT" '(' SimpleIdent ')'
	{
		$$ = &ast.DefaultExpr{Name: $3.(*ast.ColumnNameExpr).Name}
	}
|	"VALUES" '(' SimpleIdent ')' %prec lowerThanInsertValues
	{
		$$ = &ast.ValuesExpr{Column: $3.(*ast.ColumnNameExpr)}
	}

DistinctKwd:
	"DISTINCT"
|	"DISTINCTROW"

DistinctOpt:
	"ALL"
	{
		$$ = false
	}
|	DistinctKwd
	{
		$$ = true
	}

DefaultFalseDistinctOpt:
	{
		$$ = false
	}
|	DistinctOpt

DefaultTrueDistinctOpt:
	{
		$$ = true
	}
|	DistinctOpt


FunctionNameConflict:
	"ASCII"
|	"CHARSET"
|	"COALESCE"
|	"COLLATION"
|	"DATE"
|	"DATABASE"
|	"DAY"
|	"HOUR"
|	"IF"
|	"INTERVAL" %prec lowerThanIntervalKeyword
|	"FORMAT"
|	"LEFT"
|	"MICROSECOND"
|	"MINUTE"
|	"MONTH"
|	builtinNow
|	"QUARTER"
|	"REPEAT"
|	"REPLACE"
|	"REVERSE"
|	"RIGHT"
|	"ROW_COUNT"
|	"SECOND"
|	"TIME"
|	"TIMESTAMP"
|	"TRUNCATE"
|	"USER"
|	"WEEK"
|	"YEAR"

OptionalBraces:
	{} | '(' ')' {}

FunctionNameOptionalBraces:
	"CURRENT_USER"
|	"CURRENT_DATE"
|	"CURRENT_ROLE"
|	"UTC_DATE"

FunctionNameDatetimePrecision:
	"CURRENT_TIME"
|	"CURRENT_TIMESTAMP"
|	"LOCALTIME"
|	"LOCALTIMESTAMP"
|	"UTC_TIME"
|	"UTC_TIMESTAMP"

FunctionCallKeyword:
	FunctionNameConflict '(' ExpressionListOpt ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
	}
|	builtinUser '('	ExpressionListOpt ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
	}
|	FunctionNameOptionalBraces OptionalBraces
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)}
	}
|	builtinCurDate '(' ')'
    {
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)}
    }
|	FunctionNameDatetimePrecision FuncDatetimePrec
	{
		args := []ast.ExprNode{}
		if $2 != nil {
			args = append(args, $2.(ast.ExprNode))
		}
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: args}
	}
|	"MOD" '(' BitExpr ',' BitExpr ')'
	{
		$$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $3, R: $5}
	}

FunctionCallNonKeyword:
	builtinCurTime '(' FuncDatetimePrecListOpt ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
	}
|	builtinSysDate '(' FuncDatetimePrecListOpt ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
	}
|	builtinPosition '(' BitExpr "IN" Expression ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}}
	}
|	builtinSubstring '(' Expression ',' Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3, $5},
		}
	}
|	builtinSubstring '(' Expression "FROM" Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3, $5},
		}
	}
|	builtinSubstring '(' Expression ',' Expression ',' Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3, $5, $7},
		}
	}
|	builtinSubstring '(' Expression "FROM" Expression "FOR" Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3, $5, $7},
		}
	}
|	builtinTrim '(' Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$3},
		}
	}
|	builtinTrim '(' Expression "FROM" Expression ')'
	{
		$$ = &ast.FuncCallExpr{
			FnName: model.NewCIStr($1),
			Args: []ast.ExprNode{$5, $3},
		}
	}

FunctionNameDateArith:
	builtinDateAdd
|	builtinDateSub


FunctionNameDateArithMultiForms:
	builtinAddDate
|	builtinSubDate

SumExpr:
	"AVG" '(' Expression ')'
	{
		$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
	}
|	builtinCount '(' Expression ')'
	{
		$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
	}
|	builtinCount '(' '*' ')'
	{
		args := []ast.ExprNode{ast.NewValueExpr(1)}
		$$ = &ast.AggregateFuncExpr{F: $1, Args: args,}
	}
|	builtinMax '(' Expression ')'
	{
		$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
	}
|	builtinMin '(' Expression ')'
	{
		$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
	}
|	builtinSum '(' Expression ')'
	{
		$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
	}

OptGConcatSeparator:
        {
            	$$ = ast.NewValueExpr(",")
        }
| "SEPARATOR" stringLit
	{
		$$ = ast.NewValueExpr($2)
	}


FunctionCallGeneric:
	identifier '(' ExpressionListOpt ')'
	{
		$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
	}

FuncDatetimePrec:
	{
		$$ = nil
	}
|	'(' ')'
	{
		$$ = nil
	}
|	'(' intLit ')'
	{
		expr := ast.NewValueExpr($2)
		$$ = expr
	}

ExpressionOpt:
	{
		$$ = nil
	}
|	Expression
	{
		$$ = $1
	}

CastType:
	"BINARY" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeVarString)
		x.Flen = $2.(int)  // TODO: Flen should be the flen of expression
		if x.Flen != types.UnspecifiedLength {
			x.Tp = mysql.TypeString
		}
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"CHAR" OptFieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeVarString)
		x.Flen = $2.(int)  // TODO: Flen should be the flen of expression
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary{
			x.Flag |= mysql.BinaryFlag
		}
		if x.Charset == "" {
			x.Charset = mysql.DefaultCharset
			x.Collate = mysql.DefaultCollationName
		}
		$$ = x
	}
|	"DATE"
	{
		x := types.NewFieldType(mysql.TypeDate)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"DATETIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDatetime)
		x.Flen, _ = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDatetime)
		x.Decimal = $2.(int)
		if x.Decimal > 0 {
			x.Flen = x.Flen + 1 + x.Decimal
		}
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"DECIMAL" FloatOpt
	{
		fopt := $2.(*ast.FloatOpt)
		x := types.NewFieldType(mysql.TypeNewDecimal)
		x.Flen = fopt.Flen
		x.Decimal = fopt.Decimal
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"TIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDuration)
		x.Flen, _ = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDuration)
		x.Decimal = $2.(int)
		if x.Decimal > 0 {
			x.Flen = x.Flen + 1 + x.Decimal
		}
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"SIGNED" OptInteger
	{
		x := types.NewFieldType(mysql.TypeLonglong)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"UNSIGNED" OptInteger
	{
		x := types.NewFieldType(mysql.TypeLonglong)
		x.Flag |= mysql.UnsignedFlag | mysql.BinaryFlag
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		$$ = x
	}
|	"JSON"
	{
		x := types.NewFieldType(mysql.TypeJSON)
		x.Flag |= mysql.BinaryFlag | (mysql.ParseToJSONFlag)
		x.Charset = mysql.DefaultCharset
		x.Collate = mysql.DefaultCollationName
		$$ = x
	}
|	"DOUBLE"
	{
		x := types.NewFieldType(mysql.TypeDouble)
		x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDouble)
		x.Flag |= mysql.BinaryFlag
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		$$ = x
	}
|	"FLOAT" FloatOpt
	{
		x := types.NewFieldType(mysql.TypeFloat)
		fopt := $2.(*ast.FloatOpt)
		if fopt.Flen >= 54 {
			yylex.AppendError(ErrTooBigPrecision.GenWithStackByArgs(fopt.Flen,"CAST",53))
		} else if fopt.Flen >= 25 {
			x = types.NewFieldType(mysql.TypeDouble)
		}
		x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(x.Tp)
		x.Flag |= mysql.BinaryFlag
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		$$ = x
	}
|	"REAL"
	{
		var x *types.FieldType
		if parser.lexer.GetSQLMode().HasRealAsFloatMode() {
			x = types.NewFieldType(mysql.TypeFloat)
		} else {
			x = types.NewFieldType(mysql.TypeDouble)
		}
		x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(x.Tp)
		x.Flag |= mysql.BinaryFlag
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		$$ = x
	}

PriorityOpt:
	{
		$$ = mysql.NoPriority
	}
|	"LOW_PRIORITY"
	{
		$$ = mysql.LowPriority
	}
|	"HIGH_PRIORITY"
	{
		$$ = mysql.HighPriority
	}
|	"DELAYED"
	{
		$$ = mysql.DelayedPriority
	}

TableName:
	Identifier
	{
		$$ = &ast.TableName{Name:model.NewCIStr($1)}
	}
|	Identifier '.' Identifier
	{
		$$ = &ast.TableName{Schema:model.NewCIStr($1),	Name:model.NewCIStr($3)}
	}

TableNameList:
	TableName
	{
		tbl := []*ast.TableName{$1.(*ast.TableName)}
		$$ = tbl
	}
|	TableNameList ',' TableName
	{
		$$ = append($1.([]*ast.TableName), $3.(*ast.TableName))
	}

TableNameOptWild:
	Identifier OptWild
	{
		$$ = &ast.TableName{Name:model.NewCIStr($1)}
	}
|	Identifier '.' Identifier OptWild
	{
		$$ = &ast.TableName{Schema:model.NewCIStr($1),	Name:model.NewCIStr($3)}
	}

TableAliasRefList:
	TableNameOptWild
	{
		tbl := []*ast.TableName{$1.(*ast.TableName)}
		$$ = tbl
	}
|	TableAliasRefList ',' TableNameOptWild
	{
		$$ = append($1.([]*ast.TableName), $3.(*ast.TableName))
	}

OptWild:
	%prec empty
	{
	}
|	'.' '*'
	{
	}

QuickOptional:
	%prec empty
	{
		$$ = false
	}
|	"QUICK"
	{
		$$ = true
	}

RollbackStmt:
	"ROLLBACK"
	{
		$$ = &ast.RollbackStmt{}
	}

SelectStmtBasic:
	"SELECT" SelectStmtOpts SelectStmtFieldList
	{
		st := &ast.SelectStmt {
			SelectStmtOpts: $2.(*ast.SelectStmtOpts),
			Distinct:      $2.(*ast.SelectStmtOpts).Distinct,
			Fields:        $3.(*ast.FieldList),
		}
		if st.SelectStmtOpts.TableHints != nil {
			st.TableHints = st.SelectStmtOpts.TableHints
		}
		$$ = st
	}

SelectStmtFromDualTable:
	SelectStmtBasic FromDual WhereClauseOptional
	{
		st := $1.(*ast.SelectStmt)
		lastField := st.Fields.Fields[len(st.Fields.Fields)-1]
		if lastField.Expr != nil && lastField.AsName.O == "" {
			lastEnd := yyS[yypt-1].offset-1
			lastField.SetText(parser.src[lastField.Offset:lastEnd])
		}
		if $3 != nil {
			st.Where = $3.(ast.ExprNode)
		}
	}

SelectStmtFromTable:
	SelectStmtBasic "FROM"
	TableRefsClause WhereClauseOptional SelectStmtGroup HavingClause
	{
		st := $1.(*ast.SelectStmt)
		st.From = $3.(*ast.TableRefsClause)
		lastField := st.Fields.Fields[len(st.Fields.Fields)-1]
		if lastField.Expr != nil && lastField.AsName.O == "" {
			lastEnd := parser.endOffset(&yyS[yypt-4])
			lastField.SetText(parser.src[lastField.Offset:lastEnd])
		}
		if $4 != nil {
			st.Where = $4.(ast.ExprNode)
		}
		if $5 != nil {
			st.GroupBy = $5.(*ast.GroupByClause)
		}
		if $6 != nil {
			st.Having = $6.(*ast.HavingClause)
		}
		$$ = st
	}

SelectStmt:
	SelectStmtBasic OrderByOptional SelectStmtLimit
	{
		st := $1.(*ast.SelectStmt)
		lastField := st.Fields.Fields[len(st.Fields.Fields)-1]
		if lastField.Expr != nil && lastField.AsName.O == "" {
			src := parser.src
			var lastEnd int
			if $2 != nil {
				lastEnd = yyS[yypt-1].offset-1
			} else if $3 != nil {
				lastEnd = yyS[yypt-0].offset-1
			} else {
				lastEnd = len(src)
				if src[lastEnd-1] == ';' {
					lastEnd--
				}
			}
			lastField.SetText(src[lastField.Offset:lastEnd])
		}
		if $2 != nil {
			st.OrderBy = $2.(*ast.OrderByClause)
		}
		if $3 != nil {
			st.Limit = $3.(*ast.Limit)
		}
		$$ = st
	}
|	SelectStmtFromDualTable OrderByOptional SelectStmtLimit
	{
		st := $1.(*ast.SelectStmt)
		if $2 != nil {
			st.OrderBy = $2.(*ast.OrderByClause)
		}
		if $3 != nil {
			st.Limit = $3.(*ast.Limit)
		}
		$$ = st
	}
|	SelectStmtFromTable OrderByOptional SelectStmtLimit
	{
		st := $1.(*ast.SelectStmt)
		if $2 != nil {
			st.OrderBy = $2.(*ast.OrderByClause)
		}
		if $3 != nil {
			st.Limit = $3.(*ast.Limit)
		}
		$$ = st
	}

FromDual:
	"FROM" "DUAL"

TableRefsClause:
	TableRefs
	{
		$$ = &ast.TableRefsClause{TableRefs: $1.(*ast.Join)}
	}

TableRefs:
	EscapedTableRef
	{
		if j, ok := $1.(*ast.Join); ok {
			// if $1 is Join, use it directly
			$$ = j
		} else {
			$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: nil}
		}
	}
|	TableRefs ',' EscapedTableRef
	{
		/* from a, b is default cross join */
		$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin}
	}

EscapedTableRef:
	TableRef %prec lowerThanSetKeyword
	{
		$$ = $1
	}
|	'{' Identifier TableRef '}'
	{
		/*
		* ODBC escape syntax for outer join is { OJ join_table }
		* Use an Identifier for OJ
		*/
		$$ = $3
	}

TableRef:
	TableFactor
	{
		$$ = $1
	}
|	JoinTable
	{
		$$ = $1
	}

TableFactor:
	TableName TableAsNameOpt IndexHintListOpt
	{
		tn := $1.(*ast.TableName)
		tn.IndexHints = $3.([]*ast.IndexHint)
		$$ = &ast.TableSource{Source: tn, AsName: $2.(model.CIStr)}
	}
|	'(' SelectStmt ')' TableAsName
	{
		st := $2.(*ast.SelectStmt)
		endOffset := parser.endOffset(&yyS[yypt-1])
		parser.setLastSelectFieldText(st, endOffset)
		$$ = &ast.TableSource{Source: $2.(*ast.SelectStmt), AsName: $4.(model.CIStr)}
	}
|	'(' TableRefs ')'
	{
		$$ = $2
	}

TableAsNameOpt:
	{
		$$ = model.CIStr{}
	}
|	TableAsName
	{
		$$ = $1
	}

TableAsName:
	Identifier
	{
		$$ = model.NewCIStr($1)
	}
|	"AS" Identifier
	{
		$$ = model.NewCIStr($2)
	}

IndexHintType:
	"USE" KeyOrIndex
	{
		$$ = ast.HintUse
	}
|	"IGNORE" KeyOrIndex
	{
		$$ = ast.HintIgnore
	}
|	"FORCE" KeyOrIndex
	{
		$$ = ast.HintForce
	}

IndexHintScope:
	{
		$$ = ast.HintForScan
	}
|	"FOR" "JOIN"
	{
		$$ = ast.HintForJoin
	}
|	"FOR" "ORDER" "BY"
	{
		$$ = ast.HintForOrderBy
	}
|	"FOR" "GROUP" "BY"
	{
		$$ = ast.HintForGroupBy
	}


IndexHint:
	IndexHintType IndexHintScope '(' IndexNameList ')'
	{
		$$ = &ast.IndexHint{
			IndexNames:	$4.([]model.CIStr),
			HintType:	$1.(ast.IndexHintType),
			HintScope:	$2.(ast.IndexHintScope),
		}
	}

IndexNameList:
	{
		var nameList []model.CIStr
		$$ = nameList
	}
|	Identifier
	{
		$$ = []model.CIStr{model.NewCIStr($1)}
	}
|	IndexNameList ',' Identifier
	{
		$$ = append($1.([]model.CIStr), model.NewCIStr($3))
	}
|	"PRIMARY"
	{
		$$ = []model.CIStr{model.NewCIStr($1)}
	}
|	IndexNameList ',' "PRIMARY"
	{
		$$ = append($1.([]model.CIStr), model.NewCIStr($3))
	}

IndexHintList:
	IndexHint
	{
		$$ = []*ast.IndexHint{$1.(*ast.IndexHint)}
	}
|	IndexHintList IndexHint
 	{
 		$$ = append($1.([]*ast.IndexHint), $2.(*ast.IndexHint))
 	}

IndexHintListOpt:
	{
		var hintList []*ast.IndexHint
		$$ = hintList
	}
|	IndexHintList
	{
		$$ = $1
	}

JoinTable:
	/* Use %prec to evaluate production TableRef before cross join */
	TableRef CrossOpt TableRef %prec tableRefPriority
	{
		$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin}
	}
	/* Project 2: your code here.
	 * You can see details about JoinTable in https://dev.mysql.com/doc/refman/8.0/en/join.html
	 *
	 * joined_table: {
         *     table_reference {[INNER | CROSS] JOIN | STRAIGHT_JOIN} table_factor [join_specification]
         *   | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification
         *   | table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor
         * }
         *
	 */

JoinType:
	"LEFT"
	{
		$$ = ast.LeftJoin
	}
|	"RIGHT"
	{
		$$ = ast.RightJoin
	}

OuterOpt:
	{}
|	"OUTER"

CrossOpt:
	"JOIN"
|	"INNER" "JOIN"


LimitClause:
	{
		$$ = nil
	}
|	"LIMIT" LimitOption
	{
		$$ = &ast.Limit{Count: $2.(ast.ValueExpr)}
	}

LimitOption:
	LengthNum
	{
		$$ = ast.NewValueExpr($1)
	}

SelectStmtLimit:
	{
		$$ = nil
	}
|	"LIMIT" LimitOption
	{
		$$ = &ast.Limit{Count: $2.(ast.ExprNode)}
	}
|	"LIMIT" LimitOption ',' LimitOption
	{
		$$ = &ast.Limit{Offset: $2.(ast.ExprNode), Count: $4.(ast.ExprNode)}
	}
|	"LIMIT" LimitOption "OFFSET" LimitOption
	{
		$$ = &ast.Limit{Offset: $4.(ast.ExprNode), Count: $2.(ast.ExprNode)}
	}


SelectStmtOpts:
	TableOptimizerHints DefaultFalseDistinctOpt PriorityOpt SelectStmtSQLSmallResult SelectStmtSQLBigResult SelectStmtSQLBufferResult SelectStmtSQLCache SelectStmtCalcFoundRows SelectStmtStraightJoin
	{
		opt := &ast.SelectStmtOpts{}
		if $1 != nil {
			opt.TableHints = $1.([]*ast.TableOptimizerHint)
		}
		if $2 != nil {
			opt.Distinct = $2.(bool)
		}
		if $3 != nil {
			opt.Priority = $3.(mysql.PriorityEnum)
		}
		if $4 != nil {
			opt.SQLSmallResult = $4.(bool)
		}
		if $5 != nil {
			opt.SQLBigResult = $5.(bool)
		}
		if $6 != nil {
			opt.SQLBufferResult = $6.(bool)
		}
		if $7 != nil {
			opt.SQLCache = $7.(bool)
		}
		if $8 != nil {
			opt.CalcFoundRows = $8.(bool)
		}
		if $9 != nil {
			opt.StraightJoin = $9.(bool)
		}

		$$ = opt
	}

TableOptimizerHints:
	/* empty */
	{
		$$ = nil
	}
|	hintBegin OptimizerHintList hintEnd
	{
		$$ = $2
	}
|	hintBegin error hintEnd
	{
		yyerrok()
		parser.lastErrorAsWarn()
		$$ = nil
	}

OptimizerHintList:
	TableOptimizerHintOpt
	{
		$$ = []*ast.TableOptimizerHint{$1.(*ast.TableOptimizerHint)}
	}
|	StorageOptimizerHintOpt
	{
		$$ = $1.([]*ast.TableOptimizerHint)
	}
|	OptimizerHintList TableOptimizerHintOpt
	{
		$$ = append($1.([]*ast.TableOptimizerHint), $2.(*ast.TableOptimizerHint))
	}
|	OptimizerHintList ',' TableOptimizerHintOpt
	{
		$$ = append($1.([]*ast.TableOptimizerHint), $3.(*ast.TableOptimizerHint))
	}
|	OptimizerHintList StorageOptimizerHintOpt
	{
		$$ = append($1.([]*ast.TableOptimizerHint), $2.([]*ast.TableOptimizerHint)...)
	}
|	OptimizerHintList ',' StorageOptimizerHintOpt
	{
		$$ = append($1.([]*ast.TableOptimizerHint), $3.([]*ast.TableOptimizerHint)...)
	}

TableOptimizerHintOpt:
	hintUseIndex '(' QueryBlockOpt HintTable IndexNameList ')'
	{
		$$ = &ast.TableOptimizerHint{
			HintName: model.NewCIStr($1),
			QBName:   $3.(model.CIStr),
			Tables:   []ast.HintTable{$4.(ast.HintTable)},
			Indexes:  $5.([]model.CIStr),
		}
	}
|	hintIgnoreIndex '(' QueryBlockOpt HintTable IndexNameList ')'
	{
		$$ = &ast.TableOptimizerHint{
			HintName: model.NewCIStr($1),
			QBName:   $3.(model.CIStr),
			Tables:   []ast.HintTable{$4.(ast.HintTable)},
			Indexes:  $5.([]model.CIStr),
		}
	}
|	hintSMJ '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintINLJ '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintINLHJ '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintSJI '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintNSJI '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintINLMJ '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintHJ '(' QueryBlockOpt HintTableList ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)}
	}
|	hintUseIndexMerge '(' QueryBlockOpt HintTable IndexNameList ')'
	{
		$$ = &ast.TableOptimizerHint{
			HintName: model.NewCIStr($1),
			QBName:   $3.(model.CIStr),
			Tables:   []ast.HintTable{$4.(ast.HintTable)},
			Indexes:  $5.([]model.CIStr),
		}
	}
|	hintUseToja '(' QueryBlockOpt HintTrueOrFalse ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), HintFlag: $4.(bool)}
	}
|	hintEnablePlanCache '(' QueryBlockOpt HintTrueOrFalse ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), HintFlag: $4.(bool)}
	}
|	maxExecutionTime '(' QueryBlockOpt NUM ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), MaxExecutionTime: getUint64FromNUM($4)}
	}
|	hintUsePlanCache '(' QueryBlockOpt ')'
	{
		// arguments not decided yet.
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintQueryType '(' QueryBlockOpt HintQueryType ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), QueryType: model.NewCIStr($4.(string))}
	}
|	hintMemoryQuota '(' QueryBlockOpt HintMemoryQuota ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), MemoryQuota: $4.(int64)}
	}
|	hintHASHAGG '(' QueryBlockOpt ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintSTREAMAGG '(' QueryBlockOpt ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintAggToCop '(' QueryBlockOpt ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintNoIndexMerge '(' QueryBlockOpt ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintReadConsistentReplica '(' QueryBlockOpt ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)}
	}
|	hintQBName '(' Identifier ')'
	{
		$$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: model.NewCIStr($3)}
	}

StorageOptimizerHintOpt:
	hintReadFromStorage '(' QueryBlockOpt HintStorageTypeAndTableList ')'
	{
		$$ = $4.([]*ast.TableOptimizerHint)
		for _, hint := range $$.([]*ast.TableOptimizerHint) {
			hint.HintName = model.NewCIStr($1)
			hint.QBName = $3.(model.CIStr)
		}
	}

HintStorageTypeAndTableList:
	HintStorageTypeAndTable
	{
		$$ = []*ast.TableOptimizerHint{$1.(*ast.TableOptimizerHint)}
	}
|	HintStorageTypeAndTableList ',' HintStorageTypeAndTable
	{
		$$ = append($1.([]*ast.TableOptimizerHint), $3.(*ast.TableOptimizerHint))
	}

HintStorageTypeAndTable:
	HintStorageType '[' HintTableList ']'
	{
		$$ = &ast.TableOptimizerHint{
			StoreType: model.NewCIStr($1.(string)),
			Tables:    $3.([]ast.HintTable),
		}
	}
	
QueryBlockOpt:
	{
		$$ = model.NewCIStr("")
	}
|	singleAtIdentifier
	{
		$$ = model.NewCIStr($1)
	}

HintTable:
	Identifier QueryBlockOpt
	{
		$$ = ast.HintTable{TableName: model.NewCIStr($1), QBName: $2.(model.CIStr)}
	}
 |	Identifier '.' Identifier QueryBlockOpt
 	{
 		$$ = ast.HintTable{DBName: model.NewCIStr($1), TableName: model.NewCIStr($3), QBName: $4.(model.CIStr)}
 	}

HintTableList:
	HintTable
	{
		$$ = []ast.HintTable{$1.(ast.HintTable)}
	}
|	HintTableList ',' HintTable
	{
		$$ = append($1.([]ast.HintTable), $3.(ast.HintTable))
	}

HintTrueOrFalse:
	"TRUE"
	{
		$$ = true
	}
|	"FALSE"
	{
		$$ = false
	}

HintStorageType:
	hintTiKV
	{
		$$ = $1
	}
|	hintTiFlash
	{
		$$ = $1
	}

HintQueryType:
	hintOLAP
	{
		$$ = $1
	}
|	hintOLTP
	{
		$$ = $1
	}

HintMemoryQuota:
	NUM Identifier
	{
		switch model.NewCIStr($2).L {
		case "mb":
			$$ = $1.(int64) * 1024 * 1024
		case "gb":
			$$ = $1.(int64) * 1024 * 1024 * 1024
		default:
			// Executor handle memory quota < 0 as no memory limit, here use it to trigger warning in TiDB.
			$$ = int64(-1)
		}
	}

SelectStmtCalcFoundRows:
	{
		$$ = false
	}
|	"SQL_CALC_FOUND_ROWS"
	{
		$$ = true
	}
SelectStmtSQLBigResult:
	%prec empty
	{
		$$ = false
	}
|	"SQL_BIG_RESULT"
	{
		$$ = true
	}
SelectStmtSQLBufferResult:
	%prec empty
	{
		$$ = false
	}
|	"SQL_BUFFER_RESULT"
	{
		$$ = true
	}
SelectStmtSQLCache:
	%prec empty
	{
		$$ = true
	}
|	"SQL_CACHE"
	{
		$$ = true
	}
|	"SQL_NO_CACHE"
	{
		$$ = false
	}
SelectStmtSQLSmallResult:
	%prec empty
	{
		$$ = false
	}
|	"SQL_SMALL_RESULT"
	{
		$$ = true
	}
SelectStmtStraightJoin:
	%prec empty
	{
		$$ = false
	}
|	"STRAIGHT_JOIN"
	{
		$$ = true
	}

SelectStmtFieldList:
	FieldList
	{
		$$ = &ast.FieldList{Fields: $1.([]*ast.SelectField)}
	}

SelectStmtGroup:
	/* EMPTY */
	{
		$$ = nil
	}
|	GroupByClause

/********************Set Statement*******************************/
SetStmt:
	"SET" VariableAssignmentList
	{
		$$ = &ast.SetStmt{Variables: $2.([]*ast.VariableAssignment)}
	}

SetExpr:
	"ON"
	{
		$$ = ast.NewValueExpr("ON")
	}
|	ExprOrDefault

EqOrAssignmentEq:
    eq | assignmentEq

VariableName:
	Identifier
|	Identifier '.' Identifier
	{
		$$ = $1 + "." + $3
	}

VariableAssignment:
	VariableName EqOrAssignmentEq SetExpr
	{
		$$ = &ast.VariableAssignment{Name: $1, Value: $3, IsSystem: true}
	}
|	"GLOBAL" VariableName EqOrAssignmentEq SetExpr
	{
		$$ = &ast.VariableAssignment{Name: $2, Value: $4, IsGlobal: true, IsSystem: true}
	}
|	"SESSION" VariableName EqOrAssignmentEq SetExpr
	{
		$$ = &ast.VariableAssignment{Name: $2, Value: $4, IsSystem: true}
	}
|	"LOCAL" VariableName EqOrAssignmentEq Expression
	{
		$$ = &ast.VariableAssignment{Name: $2, Value: $4, IsSystem: true}
	}
|	doubleAtIdentifier EqOrAssignmentEq SetExpr
	{
		v := strings.ToLower($1)
		var isGlobal bool
		if strings.HasPrefix(v, "@@global.") {
			isGlobal = true
			v = strings.TrimPrefix(v, "@@global.")
		} else if strings.HasPrefix(v, "@@session.") {
			v = strings.TrimPrefix(v, "@@session.")
		} else if strings.HasPrefix(v, "@@local.") {
			v = strings.TrimPrefix(v, "@@local.")
		} else if strings.HasPrefix(v, "@@") {
			v = strings.TrimPrefix(v, "@@")
		}
		$$ = &ast.VariableAssignment{Name: v, Value: $3, IsGlobal: isGlobal, IsSystem: true}
	}
|	singleAtIdentifier EqOrAssignmentEq Expression
	{
		v := $1
		v = strings.TrimPrefix(v, "@")
		$$ = &ast.VariableAssignment{Name: v, Value: $3}
	}

CharsetNameOrDefault:
	CharsetName
	{
		$$ = ast.NewValueExpr($1.(string))
	}
|	"DEFAULT"
	{
		$$ = &ast.DefaultExpr{}
	}

CharsetName:
	StringName
	{
		// Validate input charset name to keep the same behavior as parser of MySQL.
		name, _, err := charset.GetCharsetInfo($1.(string))
		if err != nil {
			yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs($1))
			return 1
		}
		// Use charset name returned from charset.GetCharsetInfo(),
		// to keep lower case of input for generated column restore.
		$$ = name
	}
|	binaryType
	{
		$$ = charset.CharsetBin
	}

CollationName:
	StringName
	{
		info, err := charset.GetCollationByName($1.(string))
		if err != nil {
			yylex.AppendError(err)
			return 1
		}
		$$ = info.Name
	}

VariableAssignmentList:
	{
		$$ = []*ast.VariableAssignment{}
	}
|	VariableAssignment
	{
		$$ = []*ast.VariableAssignment{$1.(*ast.VariableAssignment)}
	}
|	VariableAssignmentList ',' VariableAssignment
	{
		$$ = append($1.([]*ast.VariableAssignment), $3.(*ast.VariableAssignment))
	}

Variable:
	SystemVariable | UserVariable

SystemVariable:
	doubleAtIdentifier
	{
		v := strings.ToLower($1)
		var isGlobal bool
		explicitScope := true
		if strings.HasPrefix(v, "@@global.") {
			isGlobal = true
			v = strings.TrimPrefix(v, "@@global.")
		} else if strings.HasPrefix(v, "@@session.") {
			v = strings.TrimPrefix(v, "@@session.")
		} else if strings.HasPrefix(v, "@@local.") {
			v = strings.TrimPrefix(v, "@@local.")
		} else if strings.HasPrefix(v, "@@") {
			v, explicitScope = strings.TrimPrefix(v, "@@"), false
		}
		$$ = &ast.VariableExpr{Name: v, IsGlobal: isGlobal, IsSystem: true, ExplicitScope: explicitScope}
	}

UserVariable:
	singleAtIdentifier
	{
		v := $1
		v = strings.TrimPrefix(v, "@")
		$$ = &ast.VariableExpr{Name: v, IsGlobal: false, IsSystem: false}
	}

/****************************Admin Statement*******************************/
AdminStmt:
	"ADMIN" "SHOW" "DDL"
	{
		$$ = &ast.AdminStmt{Tp: ast.AdminShowDDL}
	}
|	"ADMIN" "SHOW" "DDL" "JOBS" WhereClauseOptional
	{
		stmt := &ast.AdminStmt{Tp: ast.AdminShowDDLJobs}
		if $5 != nil {
			stmt.Where = $5.(ast.ExprNode)
		}
		$$ = stmt
	}
|	"ADMIN" "SHOW" "DDL" "JOBS" NUM WhereClauseOptional
	{
		stmt := &ast.AdminStmt{
		    Tp: ast.AdminShowDDLJobs,
		    JobNumber: $5.(int64),
		}
		if $6 != nil {
			stmt.Where = $6.(ast.ExprNode)
		}
		$$ = stmt
	}

/****************************Show Statement*******************************/
ShowStmt:
	"SHOW" ShowTargetFilterable ShowLikeOrWhereOpt
	{
		stmt := $2.(*ast.ShowStmt)
		if $3 != nil {
			stmt.Where = $3.(ast.ExprNode)
		}
		$$ = stmt
	}
|	"SHOW" "CREATE" "TABLE" TableName
	{
		$$ = &ast.ShowStmt{
			Tp:	ast.ShowCreateTable,
			Table:	$4.(*ast.TableName),
		}
	}
|	"SHOW" "CREATE" "DATABASE" IfNotExists DBName
	{
		$$ = &ast.ShowStmt{
			Tp:	ast.ShowCreateDatabase,
			IfNotExists: $4.(bool),
			DBName:	$5.(string),
		}
	}
|	"SHOW" OptFull "PROCESSLIST"
	{
		$$ = &ast.ShowStmt{
			Tp: ast.ShowProcessList,
			Full:	$2.(bool),
		}
	}

ShowIndexKwd:
	"INDEX"
|	"INDEXES"
|	"KEYS"

FromOrIn:
"FROM" | "IN"

ShowTargetFilterable:
	"DATABASES"
	{
		$$ = &ast.ShowStmt{Tp: ast.ShowDatabases}
	}
|	OptFull "TABLES" ShowDatabaseNameOpt
	{
		$$ = &ast.ShowStmt{
			Tp:	ast.ShowTables,
			DBName:	$3.(string),
			Full:	$1.(bool),
		}
	}
|	"WARNINGS"
	{
		$$ = &ast.ShowStmt{Tp: ast.ShowWarnings}
	}
|	"ERRORS"
	{
		$$ = &ast.ShowStmt{Tp: ast.ShowErrors}
	}
|	GlobalScope "VARIABLES"
	{
		$$ = &ast.ShowStmt{
			Tp: ast.ShowVariables,
			GlobalScope: $1.(bool),
		}
	}

ShowLikeOrWhereOpt:
	{
		$$ = nil
	}
|	"WHERE" Expression
	{
		$$ = $2
	}

GlobalScope:
	{
		$$ = false
	}
|	"GLOBAL"
	{
		$$ = true
	}
|	"SESSION"
	{
		$$ = false
	}

OptFull:
	{
		$$ = false
	}
|	"FULL"
	{
		$$ = true
	}

ShowDatabaseNameOpt:
	{
		$$ = ""
	}
|	FromOrIn DBName
	{
		$$ = $2.(string)
	}

ShowTableAliasOpt:
	FromOrIn TableName
	{
		$$ = $2.(*ast.TableName)
	}

TableNameListOpt:
	%prec empty
	{
		$$ = []*ast.TableName{}
	}
|	TableNameList
	{
		$$ = $1
	}


Statement:
	EmptyStmt
|	AdminStmt
|	AlterTableStmt
|	AnalyzeTableStmt
|	BeginTransactionStmt
|	CommitStmt
|	DeleteFromStmt
|	ExplainStmt
|	CreateDatabaseStmt
|	CreateIndexStmt
|	CreateTableStmt
|	DropDatabaseStmt
|	DropIndexStmt
|	DropTableStmt
|	InsertIntoStmt
|	RollbackStmt
|	ReplaceIntoStmt
|	SelectStmt
|	SetStmt
|	ShowStmt
|	TruncateTableStmt
|	UseStmt

ExplainableStmt:
	SelectStmt
|	DeleteFromStmt
|	InsertIntoStmt
|	ReplaceIntoStmt

StatementList:
	Statement
	{
		if $1 != nil {
			s := $1
			if lexer, ok := yylex.(stmtTexter); ok {
				s.SetText(lexer.stmtText())
			}
			parser.result = append(parser.result, s)
		}
	}
|	StatementList ';' Statement
	{
		if $3 != nil {
			s := $3
			if lexer, ok := yylex.(stmtTexter); ok {
				s.SetText(lexer.stmtText())
			}
			parser.result = append(parser.result, s)
		}
	}

Constraint:
	ConstraintKeywordOpt ConstraintElem
	{
		cst := $2.(*ast.Constraint)
		if $1 != nil {
			cst.Name = $1.(string)
		}
		$$ = cst
	}

TableElement:
	ColumnDef
	{
		$$ = $1.(*ast.ColumnDef)
	}
|	Constraint
	{
		$$ = $1.(*ast.Constraint)
	}

TableElementList:
	TableElement
	{
		if $1 != nil {
			$$ = []interface{}{$1.(interface{})}
		} else {
			$$ = []interface{}{}
		}
	}
|	TableElementList ',' TableElement
	{
		if $3 != nil {
			$$ = append($1.([]interface{}), $3)
		} else {
			$$ = $1
		}
	}

TableElementListOpt:
	/* empty */ %prec lowerThanCreateTableSelect
	{
		var columnDefs []*ast.ColumnDef
		var constraints []*ast.Constraint
		$$ = &ast.CreateTableStmt{
			Cols:           columnDefs,
			Constraints:    constraints,
		}
	}
|	'(' TableElementList ')'
	{
		tes := $2.([]interface {})
		var columnDefs []*ast.ColumnDef
		var constraints []*ast.Constraint
		for _, te := range tes {
			switch te := te.(type) {
			case *ast.ColumnDef:
				columnDefs = append(columnDefs, te)
			case *ast.Constraint:
				constraints = append(constraints, te)
			}
		}
		$$ = &ast.CreateTableStmt{
			Cols:           columnDefs,
			Constraints:    constraints,
		}
	}

OptTable:
	{}
|	"TABLE"

TruncateTableStmt:
	"TRUNCATE" OptTable TableName
	{
		$$ = &ast.TruncateTableStmt{Table: $3.(*ast.TableName)}
	}

/*************************************Type Begin***************************************/
Type:
	NumericType
	{
		$$ = $1
	}
|	StringType
	{
		$$ = $1
	}
|	DateAndTimeType
	{
		$$ = $1
	}

NumericType:
	IntegerType OptFieldLen FieldOpts
	{
		// TODO: check flen 0
		x := types.NewFieldType($1.(byte))
		x.Flen = $2.(int)
		for _, o := range $3.([]*ast.TypeOpt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	BooleanType FieldOpts
	{
		// TODO: check flen 0
		x := types.NewFieldType($1.(byte))
		x.Flen = 1
		for _, o := range $2.([]*ast.TypeOpt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	FixedPointType FloatOpt FieldOpts
	{
		fopt := $2.(*ast.FloatOpt)
		x := types.NewFieldType($1.(byte))
		x.Flen = fopt.Flen
		x.Decimal = fopt.Decimal
		for _, o := range $3.([]*ast.TypeOpt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	FloatingPointType FloatOpt FieldOpts
	{
		fopt := $2.(*ast.FloatOpt)
		x := types.NewFieldType($1.(byte))
		x.Flen = fopt.Flen
		if x.Tp == mysql.TypeFloat && fopt.Decimal == types.UnspecifiedLength && x.Flen <= mysql.MaxDoublePrecisionLength {
			if x.Flen > mysql.MaxFloatPrecisionLength {
				x.Tp = mysql.TypeDouble
			}
			x.Flen = types.UnspecifiedLength
		}
		x.Decimal = fopt.Decimal
		for _, o := range $3.([]*ast.TypeOpt) {
			if o.IsUnsigned {
				x.Flag |= mysql.UnsignedFlag
			}
			if o.IsZerofill {
				x.Flag |= mysql.ZerofillFlag
			}
		}
		$$ = x
	}
|	BitValueType OptFieldLen
	{
		x := types.NewFieldType($1.(byte))
		x.Flen = $2.(int)
		if x.Flen == types.UnspecifiedLength {
			x.Flen = 1
		}
		$$ = x
	}

IntegerType:
	"TINYINT"
	{
		$$ = mysql.TypeTiny
	}
|	"SMALLINT"
	{
		$$ = mysql.TypeShort
	}
|	"MEDIUMINT"
	{
		$$ = mysql.TypeInt24
	}
|	"INT"
	{
		$$ = mysql.TypeLong
	}
|	"INT1"
	{
		$$ = mysql.TypeTiny
	}
| 	"INT2"
	{
		$$ = mysql.TypeShort
	}
| 	"INT3"
	{
		$$ = mysql.TypeInt24
	}
|	"INT4"
	{
		$$ = mysql.TypeLong
	}
|	"INT8"
	{
		$$ = mysql.TypeLonglong
	}
|	"INTEGER"
	{
		$$ = mysql.TypeLong
	}
|	"BIGINT"
	{
		$$ = mysql.TypeLonglong
	}


BooleanType:
	"BOOL"
	{
		$$ = mysql.TypeTiny
	}
|	"BOOLEAN"
	{
		$$ = mysql.TypeTiny
	}

OptInteger:
	{}
|	"INTEGER"
|	"INT"

FixedPointType:
	"DECIMAL"
	{
		$$ = mysql.TypeNewDecimal
	}
|	"NUMERIC"
	{
		$$ = mysql.TypeNewDecimal
	}
|	"FIXED"
	{
		$$ = mysql.TypeNewDecimal
	}

FloatingPointType:
	"FLOAT"
	{
		$$ = mysql.TypeFloat
	}
|	"REAL"
	{
	    if parser.lexer.GetSQLMode().HasRealAsFloatMode() {
		    $$ = mysql.TypeFloat
	    } else {
		    $$ = mysql.TypeDouble
	    }
	}
|	"DOUBLE"
	{
		$$ = mysql.TypeDouble
	}
|	"DOUBLE" "PRECISION"
	{
		$$ = mysql.TypeDouble
	}

BitValueType:
	"BIT"
	{
		$$ = mysql.TypeBit
	}

StringType:
	Char FieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int)
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	Char OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Charset = $2.(*ast.OptBinary).Charset
		if $2.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	NChar FieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int)
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	NChar OptBinary
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Charset = $2.(*ast.OptBinary).Charset
		if $2.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	Varchar FieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeVarchar)
		x.Flen = $2.(int)
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	NVarchar FieldLen OptBinary
	{
		x := types.NewFieldType(mysql.TypeVarchar)
		x.Flen = $2.(int)
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	"BINARY" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeString)
		x.Flen = $2.(int)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	"VARBINARY" FieldLen
	{
		x := types.NewFieldType(mysql.TypeVarchar)
		x.Flen = $2.(int)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		x.Flag |= mysql.BinaryFlag
		$$ = x
	}
|	BlobType
	{
		x := $1.(*types.FieldType)
		x.Charset = charset.CharsetBin
		x.Collate = charset.CharsetBin
		x.Flag |= mysql.BinaryFlag
		$$ = $1.(*types.FieldType)
	}
|	TextType OptCharsetWithOptBinary
	{
		x := $1.(*types.FieldType)
		x.Charset = $2.(*ast.OptBinary).Charset
		if $2.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	"ENUM" '(' StringList ')' OptCharset
	{
		x := types.NewFieldType(mysql.TypeEnum)
		x.Elems = $3.([]string)
		x.Charset = $5.(string)
		$$ = x
	}
|	"SET" '(' StringList ')' OptCharset
	{
		x := types.NewFieldType(mysql.TypeSet)
		x.Elems = $3.([]string)
		x.Charset = $5.(string)
		$$ = x
	}
|	"JSON"
	{
		x := types.NewFieldType(mysql.TypeJSON)
		x.Decimal = 0
		x.Charset = charset.CharsetBin
		x.Collate = charset.CollationBin
		$$ = x
	}
|	"LONG" Varchar OptCharsetWithOptBinary
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		x.Charset = $3.(*ast.OptBinary).Charset
		if $3.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}
|	"LONG" OptCharsetWithOptBinary
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		x.Charset = $2.(*ast.OptBinary).Charset
		if $2.(*ast.OptBinary).IsBinary {
			x.Flag |= mysql.BinaryFlag
		}
		$$ = x
	}

Char:
	"CHARACTER"
|	"CHAR"

NChar:
	"NCHAR"
|	"NATIONAL" "CHARACTER"
|	"NATIONAL" "CHAR"

Varchar:
	"CHARACTER" "VARYING"
|	"CHAR" "VARYING"
| 	"VARCHAR"
|	"VARCHARACTER"

NVarchar:
	"NATIONAL" "VARCHAR"
|	"NATIONAL" "VARCHARACTER"
| 	"NVARCHAR"
|	"NCHAR" "VARCHAR"
|	"NCHAR" "VARCHARACTER"
| 	"NATIONAL" "CHARACTER" "VARYING"
| 	"NATIONAL" "CHAR" "VARYING"
| 	"NCHAR" "VARYING"

Year:
	"YEAR"
|	"SQL_TSI_YEAR"


BlobType:
	"TINYBLOB"
	{
		x := types.NewFieldType(mysql.TypeTinyBlob)
		$$ = x
	}
|	"BLOB" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeBlob)
		x.Flen = $2.(int)
		$$ = x
	}
|	"MEDIUMBLOB"
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		$$ = x
	}
|	"LONGBLOB"
	{
		x := types.NewFieldType(mysql.TypeLongBlob)
		$$ = x
	}
|	"LONG" "VARBINARY"
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		$$ = x
	}

TextType:
	"TINYTEXT"
	{
		x := types.NewFieldType(mysql.TypeTinyBlob)
		$$ = x

	}
|	"TEXT" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeBlob)
		x.Flen = $2.(int)
		$$ = x
	}
|	"MEDIUMTEXT"
	{
		x := types.NewFieldType(mysql.TypeMediumBlob)
		$$ = x
	}
|	"LONGTEXT"
	{
		x := types.NewFieldType(mysql.TypeLongBlob)
		$$ = x
	}

OptCharsetWithOptBinary:
	OptBinary
	{
		$$ = $1
	}
|	"ASCII"
	{
		$$ = &ast.OptBinary{
			IsBinary: false,
			Charset:  charset.CharsetLatin1,
		}
	}
|	"UNICODE"
	{
		name, _, err := charset.GetCharsetInfo("ucs2")
		if err != nil {
			yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs("ucs2"))
			return 1
		}
		$$ = &ast.OptBinary{
			IsBinary: false,
			Charset:  name,
		}
	}
|	"BYTE"
	{
		$$ = &ast.OptBinary{
			IsBinary: false,
			Charset:  "",
		}
	}

DateAndTimeType:
	"DATE"
	{
		x := types.NewFieldType(mysql.TypeDate)
		$$ = x
	}
|	"DATETIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDatetime)
		x.Flen = mysql.MaxDatetimeWidthNoFsp
		x.Decimal = $2.(int)
		if x.Decimal > 0 {
			x.Flen = x.Flen + 1 + x.Decimal
		}
		$$ = x
	}
|	"TIMESTAMP" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeTimestamp)
		x.Flen = mysql.MaxDatetimeWidthNoFsp
		x.Decimal = $2.(int)
		if x.Decimal > 0 {
			x.Flen = x.Flen + 1 + x.Decimal
		}
		$$ = x
	}
|	"TIME" OptFieldLen
	{
		x := types.NewFieldType(mysql.TypeDuration)
		x.Flen = mysql.MaxDurationWidthNoFsp
		x.Decimal = $2.(int)
		if x.Decimal > 0 {
			x.Flen = x.Flen + 1 + x.Decimal
		}
		$$ = x
	}
|	Year OptFieldLen FieldOpts
	{
		x := types.NewFieldType(mysql.TypeYear)
		x.Flen = $2.(int)
		if x.Flen != types.UnspecifiedLength && x.Flen != 4 {
			yylex.AppendError(ErrInvalidYearColumnLength.GenWithStackByArgs())
			return -1
		}
		$$ = x
	}

FieldLen:
	'(' LengthNum ')'
	{
		$$ = int($2.(uint64))
	}

OptFieldLen:
	{
		$$ = types.UnspecifiedLength
	}
|	FieldLen
	{
		$$ = $1.(int)
	}

FieldOpt:
	"UNSIGNED"
	{
		$$ = &ast.TypeOpt{IsUnsigned: true}
	}
|	"SIGNED"
	{
		$$ = &ast.TypeOpt{IsUnsigned: false}
	}
|	"ZEROFILL"
	{
		$$ = &ast.TypeOpt{IsZerofill: true, IsUnsigned: true}
	}

FieldOpts:
	{
		$$ = []*ast.TypeOpt{}
	}
|	FieldOpts FieldOpt
	{
		$$ = append($1.([]*ast.TypeOpt), $2.(*ast.TypeOpt))
	}

FloatOpt:
	{
		$$ = &ast.FloatOpt{Flen: types.UnspecifiedLength, Decimal: types.UnspecifiedLength}
	}
|	FieldLen
	{
		$$ = &ast.FloatOpt{Flen: $1.(int), Decimal: types.UnspecifiedLength}
	}
|	Precision
	{
		$$ = $1.(*ast.FloatOpt)
	}

Precision:
	'(' LengthNum ',' LengthNum ')'
	{
		$$ = &ast.FloatOpt{Flen: int($2.(uint64)), Decimal: int($4.(uint64))}
	}

OptBinMod:
	{
		$$ = false
	}
|	"BINARY"
	{
		$$ = true
	}

OptBinary:
	{
		$$ = &ast.OptBinary{
			IsBinary: false,
			Charset:  "",
		}
	}
|	"BINARY" OptCharset
	{
		$$ = &ast.OptBinary{
			IsBinary: true,
			Charset:  $2.(string),
		}
	}
|	CharsetKw CharsetName OptBinMod
	{
		$$ = &ast.OptBinary{
			IsBinary: $3.(bool),
			Charset:  $2.(string),
		}
	}

OptCharset:
	{
		$$ = ""
	}
|	CharsetKw CharsetName
	{
		$$ = $2.(string)
	}

CharsetKw:
	"CHARACTER" "SET"
|	"CHARSET"
| 	"CHAR" "SET"

OptCollate:
	{
		$$ = ""
	}
|	"COLLATE" CollationName
	{
		$$ = $2.(string)
	}

StringList:
	stringLit
	{
		$$ = []string{$1}
	}
|	StringList ',' stringLit
	{
		$$ = append($1.([]string), $3)
	}

StringName:
	stringLit
	{
		$$ = $1
	}
|	Identifier
	{
		$$ = $1
	}

UseStmt:
	"USE" DBName
	{
		$$ = &ast.UseStmt{DBName: $2.(string)}
	}

WhereClause:
	"WHERE" Expression
	{
		$$ = $2
	}

WhereClauseOptional:
	{
		$$ = nil
	}
|	WhereClause
	{
		$$ = $1
	}

CommaOpt:
	{}
|	','
	{}
